miércoles, 23 de enero de 2013

Forzando un peche dun socket dende fora da aplicación [Unix]

Boas, hoxe topeime cun problema peculiar, andaba usando unha aplicación en rede e quedou un socket colgado, bloqueando a miña sesión (a aplicación en cuestión só permite unha por usuario) e sen poder pechar o programa por manter a conexión noutros servidores, así que non quedou máis remedio que pensar como remediar esa situación.

Para ilustrar o procedemento co que dei utilizaremos un par de scripts, un cliente e un servidor (non fai falla entendelos, de feito as variables están nomeadas o chou e tal :P), non dan ninguná saída, así que só hai que lanzalos na orde correcta (`server.py` e despois `client.py`, sen pechalo) e deixalos estar:

server.py
#!/usr/bin/env python

target = ("127.0.0.1", 1234)

from socket import socket

sock = socket()
sock.bind(target)
sock.listen(10)

sock_l = []

while True:
    sock_l.append(sock.accept())

client.py
#!/usr/bin/env python

target = ("127.0.0.1", 1234)

from socket import socket
from time import sleep

good_socket = socket() # Ignorade os nomes, non representan nada
bad_socket = socket()

good_socket.connect(target)
bad_socket.connect(target)

while True:
    sleep(1)



Pois ben, lanzamos os dous scripts (de novo, primeiro `server.py` e despois `client.py`, sen pechalo)
os dous script's nunha sesión de `screen`

O seguinte será buscar os números dos procesos e dos sockets (xa veredes de que vai), un xeito e facer netstat e buscar o porto:
netstat -tpne|grep 1234
Os flags usados en netstat son `-t` (conexións TCP), `-p` (información sobre o proceso), `-n` (non precisamos convertir as IP en nomes de dominio e así vai máis rápido) e `-e` (información extendida, coma o número de socket), por exemplo:

A primeira e a terceira conexións son do servidor o cliente (o 1234 está a esquerda), a segunda e cuarta do cliente o servidor (o 1234 está a dereita) e a segunda e a espera de conexións do servidor.

Ímos entón a cortar a última, de cliente a servidor (o procedemento é o mesmo para calquera outra), so temos que anotar o proceso (22074) e o socket (1050614). Falta un último retal de información que podemos tomar da combinación das anteriores, podemos forzar un proceso a pechar un descriptor de arquivo, pero precisamos coñecer o seu número (para isto o número de socket), para desvelalo só temos que buscar no `/proc/<número de proceso>/fd/` e buscar cal corresponde o socket, neste caso poderíamos facelo así:
file /proc/22074/fd/* |grep 1050614

O arquivo que corresponde é `/proc/22074/fd/4`, logo pecharemos o descriptor 4, con isto pasammos a última fase, tomar o control do proceso e executa-lo peche.

Para tomar o control do proceso empregaremos `gdb`(probablemente teñamos que lanzalo coma root), unha vez nel temos que facer `attach <número do proceso>` para tomar o control.

Mentres estamos neste estado o programa o programa estará detido, así que ímos avanzar rápido, temos que facer `print close(<descriptor de ficheiro>)` (`print` permítenos executar código no propio programa attacheado) e despois `detach` para libera-lo programa.


E xa está, pechamos o socket :D



Ísto é algo que me encanta dos SO Unix, podes facer de todo coas ferramentas xenéricas

No hay comentarios:

Publicar un comentario