示例#1
0
void rpcclnt_disconnect(struct rpcclnt *rpc)
{
  if (rpc->rc_so != NULL)
    {
      (void)psock_close(rpc->rc_so);
    }
}
示例#2
0
static int server_listen(void)
{
	t_addr		* curr_laddr;
	t_addr_data	laddr_data;
	int		sock;

	if (!(server_listen_addrs=addrlist_create(prefs_get_servaddrs(),INADDR_ANY,D2CS_SERVER_PORT))) {
		eventlog(eventlog_level_error,__FUNCTION__,"error create listening address list");
		return -1;
	}
	BEGIN_LIST_TRAVERSE_DATA(server_listen_addrs,curr_laddr)
	{
		sock=net_listen(addr_get_ip(curr_laddr),addr_get_port(curr_laddr),PSOCK_SOCK_STREAM);
		if (sock<0) {
			eventlog(eventlog_level_error,__FUNCTION__,"error listen socket");
			return -1;
		}

		if (psock_ctl(sock,PSOCK_NONBLOCK)<0) {
			eventlog(eventlog_level_error,__FUNCTION__,"error set listen socket in non-blocking mode");
		}

		laddr_data.i = sock;
		addr_set_data(curr_laddr,laddr_data);

		if (fdwatch_add_fd(sock, fdwatch_type_read, d2cs_server_handle_accept, curr_laddr)<0) {
		    eventlog(eventlog_level_error,__FUNCTION__,"error adding socket %d to fdwatch pool (max sockets?)",sock);
		    psock_close(sock);
		    return -1;
		}

		eventlog(eventlog_level_info,__FUNCTION__,"listen on %s", addr_num_to_addr_str(addr_get_ip(curr_laddr),addr_get_port(curr_laddr)));
	}
示例#3
0
文件: net.cpp 项目: AleXoundOS/pvpgn
extern int net_socket(int type)
{
	int	sock;
	int	val;
	int     ipproto;

	if (type==PSOCK_SOCK_STREAM) {
		ipproto = PSOCK_IPPROTO_TCP;
	} else {
		ipproto = PSOCK_IPPROTO_UDP;
	}
	if ((sock=psock_socket(PSOCK_PF_INET, type, ipproto))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error creating socket (psock_socket: %s)", pstrerror(psock_errno()));
		return -1;
	}
	val=1;
	if (psock_setsockopt(sock,PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &val, sizeof(val))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error set socket option KEEPALIVE (psock_setsockopt: %s)",pstrerror(psock_errno()));
	}
	if (psock_ctl(sock,PSOCK_NONBLOCK)<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error set socket mode to non-block (psock_ctl: %s)",pstrerror(psock_errno()));
		psock_close(sock);
		return -1;
	}
	return sock;
}
示例#4
0
文件: net.cpp 项目: AleXoundOS/pvpgn
extern int net_listen(unsigned int ip, unsigned int port, int type)
{
	int			sock;
	int			val;
	struct  sockaddr_in	addr;
	int    			ipproto;

	if (type==PSOCK_SOCK_STREAM) {
		ipproto = PSOCK_IPPROTO_TCP;
	} else {
		ipproto = PSOCK_IPPROTO_UDP;
	}
	if ((sock=psock_socket(PSOCK_PF_INET, type, ipproto))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error create listen socket");
		return -1;
	}
	val=1;
	if (psock_setsockopt(sock,PSOCK_SOL_SOCKET,PSOCK_SO_REUSEADDR,&val,sizeof(int))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error set socket option SO_REUSEADDR");
	}
	std::memset(&addr,0,sizeof(addr));
	addr.sin_family=PSOCK_AF_INET;
	addr.sin_port=htons(port);
	addr.sin_addr.s_addr=htonl(ip);
	if (psock_bind(sock,(struct sockaddr *)&addr, sizeof(addr))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error bind listen socket");
		psock_close(sock);
		return -1;
	}
	if (psock_listen(sock,LISTEN_QUEUE)<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error listen socket");
		psock_close(sock);
		return -1;
	}
	return sock;
}
示例#5
0
void net_releaselist(FAR struct socketlist *list)
{
  int ndx;

  DEBUGASSERT(list);

  /* Close each open socket in the list. */

  for (ndx = 0; ndx < CONFIG_NSOCKET_DESCRIPTORS; ndx++)
    {
      FAR struct socket *psock = &list->sl_sockets[ndx];
      if (psock->s_crefs > 0)
        {
          (void)psock_close(psock);
        }
    }

  /* Destroy the semaphore */

  (void)sem_destroy(&list->sl_sem);
}
示例#6
0
void dbs_server_loop(int lsocket)
{
	struct sockaddr_in sinRemote;
	int sd ;
	fd_set ReadFDs, WriteFDs, ExceptFDs;
	t_elem * elem;
	t_d2dbs_connection* it;
	BOOL bOK ;
	const char* pcErrorType ;
	struct timeval         tv;
	int highest_fd;
	psock_t_socklen nAddrSize = sizeof(sinRemote);
	
	while (1) {
#ifdef WIN32
		if (g_ServiceStatus<0 && kbhit() && getch()=='q')
			d2dbs_signal_quit_wrapper();
		
		if (g_ServiceStatus == 0) d2dbs_signal_quit_wrapper();
		
		while (g_ServiceStatus == 2) Sleep(1000);
#endif
		if (d2dbs_handle_signal()<0) break;
		dbs_handle_timed_events();
		highest_fd=dbs_server_setup_fdsets(&ReadFDs, &WriteFDs, &ExceptFDs, lsocket);

		tv.tv_sec  = 0;
		tv.tv_usec = SELECT_TIME_OUT;
		switch (psock_select(highest_fd+1, &ReadFDs, &WriteFDs, &ExceptFDs, &tv) ) {
			case -1:
				eventlog(eventlog_level_error,__FUNCTION__,"psock_select() failed : %s",strerror(psock_errno()));
				continue;
			case 0:
				continue;
			default:
				break;
		}

		if (PSOCK_FD_ISSET(lsocket, &ReadFDs)) {
			sd = psock_accept(lsocket, (struct sockaddr*)&sinRemote, &nAddrSize);
			if (sd == -1) {
				eventlog(eventlog_level_error,__FUNCTION__,"psock_accept() failed : %s",strerror(psock_errno()));
				return;
			}
			
			eventlog(eventlog_level_info,__FUNCTION__,"accepted connection from %s:%d , socket %d .",
				inet_ntoa(sinRemote.sin_addr) , ntohs(sinRemote.sin_port), sd);
			eventlog_step(prefs_get_logfile_gs(),eventlog_level_info,__FUNCTION__,"accepted connection from %s:%d , socket %d .",
				inet_ntoa(sinRemote.sin_addr) , ntohs(sinRemote.sin_port), sd);
			setsockopt_keepalive(sd);
			dbs_server_list_add_socket(sd, ntohl(sinRemote.sin_addr.s_addr));
			if (psock_ctl(sd,PSOCK_NONBLOCK)<0) {
				eventlog(eventlog_level_error,__FUNCTION__,"could not set TCP socket [%d] to non-blocking mode (closing connection) (psock_ctl: %s)", sd,strerror(psock_errno()));
				psock_close(sd);
			}
		} else if (PSOCK_FD_ISSET(lsocket, &ExceptFDs)) {
			eventlog(eventlog_level_error,__FUNCTION__,"exception on listening socket");
			/* FIXME: exceptions are not errors with TCP, they are out-of-band data */
			return;
		}
		
		LIST_TRAVERSE(dbs_server_connection_list,elem)
		{
			bOK = TRUE;
			pcErrorType = 0;
			
			if (!(it=elem_get_data(elem))) continue;
			if (PSOCK_FD_ISSET(it->sd, &ExceptFDs)) {
				bOK = FALSE;
				pcErrorType = "General socket error"; /* FIXME: no no no no no */
				PSOCK_FD_CLR(it->sd, &ExceptFDs);
			} else {
				
				if (PSOCK_FD_ISSET(it->sd, &ReadFDs)) {
					bOK = dbs_server_read_data(it);
					pcErrorType = "Read error";
					PSOCK_FD_CLR(it->sd, &ReadFDs);
				}
				
				if (PSOCK_FD_ISSET(it->sd, &WriteFDs)) {
					bOK = dbs_server_write_data(it);
					pcErrorType = "Write error";
					PSOCK_FD_CLR(it->sd, &WriteFDs);
				}
			}
			
			if (!bOK) {
				int	err;
				psock_t_socklen	errlen;
				
				err = 0;
				errlen = sizeof(err);
				if (psock_getsockopt(it->sd, PSOCK_SOL_SOCKET, PSOCK_SO_ERROR, &err, &errlen)==0) {
					if (errlen && err!=0) {
						eventlog(eventlog_level_error,__FUNCTION__,"data socket error : %s",strerror(err));
					}
				}
				dbs_server_shutdown_connection(it);
				list_remove_elem(dbs_server_connection_list,&elem);
			} else {
				if (dbs_packet_handle(it)==-1) {
					eventlog(eventlog_level_error,__FUNCTION__,"dbs_packet_handle() failed");
					dbs_server_shutdown_connection(it);
					list_remove_elem(dbs_server_connection_list,&elem);
				}
			}
		}
	}
示例#7
0
文件: my_api.cpp 项目: 0b1kn00b/hxcpp
static void myp_close( MYSQL *m ) {
	psock_close(m->s);
	m->s = INVALID_SOCKET;
}
示例#8
0
extern t_connection * s2s_create(char const * server, unsigned short def_port, t_conn_class cclass)
{
	struct sockaddr_in	addr, laddr;
	psock_t_socklen		laddr_len;
	unsigned int		ip;
	unsigned short		port;
	int			sock, connected;
	t_connection		* c;
	char			* p, * tserver;

	ASSERT(server,NULL);
	tserver=xstrdup(server);
	p=std::strchr(tserver,':');
	if (p) {
		port=(unsigned short)std::strtoul(p+1,NULL,10);
		*p='\0';
	} else {
		port=def_port;
	}

	if ((sock=net_socket(PSOCK_SOCK_STREAM))<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"error creating s2s socket");
		xfree(tserver);
		return NULL;
	}

	std::memset(&addr,0,sizeof(addr));
	addr.sin_family = PSOCK_AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr= net_inet_addr(tserver);
	xfree(tserver);

	eventlog(eventlog_level_info,__FUNCTION__,"try make s2s connection to {}",server);
	if (psock_connect(sock,(struct sockaddr *)&addr,sizeof(addr))<0) {
		if (psock_errno()!=PSOCK_EWOULDBLOCK && psock_errno() != PSOCK_EINPROGRESS) {
			eventlog(eventlog_level_error,__FUNCTION__,"error connecting to {} (psock_connect: {})",server,pstrerror(psock_errno()));
			psock_close(sock);
			return NULL;
		}
		connected=0;
		eventlog(eventlog_level_info,__FUNCTION__,"connection to s2s server {} is in progress",server);
	} else {
		connected=1;
		eventlog(eventlog_level_info,__FUNCTION__,"connected to s2s server {}",server);
	}
	laddr_len=sizeof(laddr);
	std::memset(&laddr,0,sizeof(laddr));
	ip=port=0;
	if (psock_getsockname(sock,(struct sockaddr *)&laddr,&laddr_len)<0) {
		eventlog(eventlog_level_error,__FUNCTION__,"unable to get local socket info");
	} else {
		if (laddr.sin_family != PSOCK_AF_INET) {
			eventlog(eventlog_level_error,__FUNCTION__,"got bad socket family {}",laddr.sin_family);
		} else {
			ip=ntohl(laddr.sin_addr.s_addr);
			port=ntohs(laddr.sin_port);
		}
	}
	if (!(c=d2cs_conn_create(sock,ip,port, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)))) {
		eventlog(eventlog_level_error,__FUNCTION__,"error create s2s connection");
		psock_close(sock);
		return NULL;
	}
	if (connected) {
		if (conn_add_fd(c,fdwatch_type_read, d2cs_server_handle_tcp)<0) {
		    eventlog(eventlog_level_error, __FUNCTION__, "error adding socket {} to fdwatch pool (max sockets?)",sock);
		    d2cs_conn_set_state(c,conn_state_destroy);
		    return NULL;
		}
		d2cs_conn_set_state(c,conn_state_init);
	} else {
		if (conn_add_fd(c, fdwatch_type_write, d2cs_server_handle_tcp)<0) {
		    eventlog(eventlog_level_error, __FUNCTION__, "error adding socket {} to fdwatch pool (max sockets?)",sock);
		    d2cs_conn_set_state(c,conn_state_destroy);
		    return NULL;
		}
		d2cs_conn_set_state(c,conn_state_connecting);
	}
	d2cs_conn_set_class(c,cclass);
	return c;
}
示例#9
0
int net_close(int sockfd)
{
  return psock_close(sockfd_socket(sockfd));
}
示例#10
0
static int proxy_process(unsigned short server_listen_port, struct sockaddr_in servaddr)
{
    int                lsock;
    struct sockaddr_in laddr;
    t_psock_fd_set     rfds, wfds;
    int                highest_fd;
    int                udpsock;
    t_virtconn *       vc;
    t_elem const *     curr;
    int                csocket;
    int                ssocket;
    
    if ((udpsock = psock_socket(PSOCK_PF_INET,PSOCK_SOCK_DGRAM,PSOCK_IPPROTO_UDP))<0)
    {
	eventlog(eventlog_level_error,__FUNCTION__,"could not create UDP socket (psock_socket: %s)",pstrerror(psock_errno()));
	return -1;
    }
    if (psock_ctl(udpsock,PSOCK_NONBLOCK)<0)
	eventlog(eventlog_level_error,__FUNCTION__,"could not set UDP listen socket to non-blocking mode (psock_ctl: %s)",pstrerror(psock_errno()));
    
    if ((lsock = psock_socket(PSOCK_PF_INET,PSOCK_SOCK_STREAM,PSOCK_IPPROTO_TCP))<0)
    {
	eventlog(eventlog_level_error,__FUNCTION__,"could not create listening socket (psock_socket: %s)",pstrerror(psock_errno()));
        psock_close(udpsock);
	return -1;
    }
    
    {
	int val=1;
	
	if (psock_setsockopt(lsock,PSOCK_SOL_SOCKET,PSOCK_SO_REUSEADDR,&val,(psock_t_socklen)sizeof(int))<0)
	    eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not set socket option SO_REUSEADDR (psock_setsockopt: %s)",lsock,pstrerror(psock_errno()));
	/* not a fatal error... */
    }
    
    memset(&laddr,0,sizeof(laddr));
    laddr.sin_family = PSOCK_AF_INET;
    laddr.sin_port = htons(server_listen_port);
    laddr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (psock_bind(lsock,(struct sockaddr *)&laddr,(psock_t_socklen)sizeof(laddr))<0)
    {
	eventlog(eventlog_level_error,__FUNCTION__,"could not bind socket to address 0.0.0.0:%hu TCP (psock_bind: %s)",server_listen_port,pstrerror(psock_errno()));
        psock_close(udpsock);
	psock_close(lsock);
	return -1;
    }
    
    memset(&laddr,0,sizeof(laddr));
    laddr.sin_family = PSOCK_AF_INET;
    laddr.sin_port = htons(server_listen_port);
    laddr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (psock_bind(udpsock,(struct sockaddr *)&laddr,(psock_t_socklen)sizeof(laddr))<0)
    {
	eventlog(eventlog_level_error,__FUNCTION__,"could not bind socket to address 0.0.0.0:%hu UDP (psock_bind: %s)",server_listen_port,pstrerror(psock_errno()));
	psock_close(udpsock);
	psock_close(lsock);
	return -1;
    }
    eventlog(eventlog_level_info,__FUNCTION__,"bound to UDP port %hu",server_listen_port);
    
    /* tell socket to listen for connections */
    if (psock_listen(lsock,LISTEN_QUEUE)<0)
    {
	eventlog(eventlog_level_error,__FUNCTION__,"could not listen (psock_listen: %s)",pstrerror(psock_errno()));
        psock_close(udpsock);
	psock_close(lsock);
	return -1;
    }
    if (psock_ctl(lsock,PSOCK_NONBLOCK)<0)
	eventlog(eventlog_level_error,__FUNCTION__,"could not set TCP listen socket to non-blocking mode (psock_ctl: %s)",pstrerror(psock_errno()));
    
    eventlog(eventlog_level_info,__FUNCTION__,"listening on TCP port %hu",server_listen_port);
    
    for (;;)
    {
	/* loop over all connections to create the sets for select() */
	PSOCK_FD_ZERO(&rfds);
	PSOCK_FD_ZERO(&wfds);
	highest_fd = lsock;
	PSOCK_FD_SET(lsock,&rfds);
	if (udpsock>highest_fd)
	    highest_fd = udpsock;
	PSOCK_FD_SET(udpsock,&rfds);
	
	LIST_TRAVERSE_CONST(virtconnlist(),curr)
	{
	    vc = elem_get_data(curr);
            csocket = virtconn_get_client_socket(vc);
	    if (queue_get_length((t_queue const * const *)virtconn_get_clientout_queue(vc))>0)
	        PSOCK_FD_SET(csocket,&wfds); /* pending output, also check for writeability */
	    PSOCK_FD_SET(csocket,&rfds);
            
	    if (csocket>highest_fd)
                highest_fd = csocket;
	    
	    switch (virtconn_get_state(vc))
	    {
	    case virtconn_state_connecting:
		eventlog(eventlog_level_debug,__FUNCTION__,"waiting for %d to finish connecting",ssocket);
		ssocket = virtconn_get_server_socket(vc);
		PSOCK_FD_SET(ssocket,&wfds); /* wait for connect to complete */
		
		if (ssocket>highest_fd)
		    highest_fd = ssocket;
		break;
	    case virtconn_state_connected:
		eventlog(eventlog_level_debug,__FUNCTION__,"checking for reading on connected socket %d",ssocket);
		ssocket = virtconn_get_server_socket(vc);
		if (queue_get_length((t_queue const * const *)virtconn_get_serverout_queue(vc))>0)
		    PSOCK_FD_SET(ssocket,&wfds); /* pending output, also check for writeability */
		PSOCK_FD_SET(ssocket,&rfds);
		
		if (ssocket>highest_fd)
		    highest_fd = ssocket;
		break;
	    default: /* avoid warning */
		break;
	    }
	}
	
	/* find which sockets need servicing */
	if (psock_select(highest_fd+1,&rfds,&wfds,NULL,NULL)<0)
	{
	    if (errno!=PSOCK_EINTR)
	        eventlog(eventlog_level_error,__FUNCTION__,"select failed (select: %s)",pstrerror(errno));
	    continue;
	}
	
	/* check for incoming connection */
	if (PSOCK_FD_ISSET(lsock,&rfds))
	{
            int                asock;
	    struct sockaddr_in caddr;
	    psock_t_socklen    caddr_len;
            
	    /* accept the connection */
	    caddr_len = sizeof(caddr);
	    if ((asock = psock_accept(lsock,(struct sockaddr *)&caddr,&caddr_len))<0)
	    {
		if (psock_errno()==PSOCK_EWOULDBLOCK || psock_errno()==PSOCK_ECONNABORTED) /* BSD, POSIX error for aborted connections, SYSV often uses EAGAIN */
		    eventlog(eventlog_level_error,__FUNCTION__,"client aborted connection (psock_accept: %s)",pstrerror(psock_errno()));
		else /* EAGAIN can mean out of resources _or_ connection aborted */
		    if (psock_errno()!=PSOCK_EINTR)
			eventlog(eventlog_level_error,__FUNCTION__,"could not accept new connection (psock_accept: %s)",pstrerror(psock_errno()));
	    }
	    else
	    {
		int ssd;
		int val=1;
		
		eventlog(eventlog_level_info,__FUNCTION__,"[%d] accepted connection from %s:%hu",asock,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
		
		if (psock_setsockopt(asock,PSOCK_SOL_SOCKET,PSOCK_SO_KEEPALIVE,&val,(psock_t_socklen)sizeof(val))<0)
		    eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not set socket option SO_KEEPALIVE (psock_setsockopt: %s)",asock,pstrerror(psock_errno()));
		    /* not a fatal error */
		
		if (psock_ctl(asock,PSOCK_NONBLOCK)<0)
		{
		    eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: %s)",asock,pstrerror(psock_errno()));
		    psock_close(asock);
		}
		else
		    if ((ssd = psock_socket(PSOCK_PF_INET,PSOCK_SOCK_STREAM,PSOCK_IPPROTO_TCP))<0)
		    {
			eventlog(eventlog_level_error,__FUNCTION__,"[%d] could create TCP socket (closing connection) (psock_socket: %s)",asock,pstrerror(psock_errno()));
			psock_close(asock);
		    }
		    else
			if (psock_ctl(ssd,PSOCK_NONBLOCK)<0)
			{
			    eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: %s)",asock,pstrerror(psock_errno()));
			    psock_close(ssd);
			    psock_close(asock);
			}
			else
			    if (!(vc = virtconn_create(asock,ssd,ntohl(caddr.sin_addr.s_addr),BNETD_MIN_TEST_PORT)))
			    {
				eventlog(eventlog_level_error,__FUNCTION__,"[%d] unable to create new connection (closing connection)",asock);
				psock_close(ssd);
				psock_close(asock);
			    }
			    else
			    {
				memset(&caddr,0,sizeof(caddr));
				caddr.sin_family = PSOCK_AF_INET;
				caddr.sin_port = htons(virtconn_get_udpport(vc));
				caddr.sin_addr.s_addr = htonl(virtconn_get_udpaddr(vc));
				eventlog(eventlog_level_info,__FUNCTION__,"[%d] addr now %s:%hu",asock,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
			    }
	    }
	}
	
	eventlog(eventlog_level_debug,__FUNCTION__,"checking for incoming UDP");
	if (PSOCK_FD_ISSET(udpsock,&rfds))
	{
	    t_packet *         upacket;
	    struct sockaddr_in toaddr;
	    struct sockaddr_in fromaddr;
	    psock_t_socklen    fromlen;
	    int                len;
	    
	    if (!(upacket = packet_create(packet_class_raw)))
		eventlog(eventlog_level_error,__FUNCTION__,"could not allocate raw packet for input");
	    else
	    {
		/* packet_set_flags(upacket,PROXY_FLAG_UDP);*/
		
		fromlen = sizeof(fromaddr);
		if ((len = psock_recvfrom(udpsock,packet_get_raw_data_build(upacket,0),MAX_PACKET_SIZE,0,(struct sockaddr *)&fromaddr,&fromlen))<0)
		{
		    if (psock_errno()!=PSOCK_EINTR && psock_errno()!=PSOCK_EAGAIN && psock_errno()!=PSOCK_EWOULDBLOCK)
			eventlog(eventlog_level_error,__FUNCTION__,"could not recv UDP datagram (psock_recvfrom: %s)",pstrerror(psock_errno()));
		}
		else
		{
		    if (fromaddr.sin_family!=PSOCK_AF_INET)
			eventlog(eventlog_level_error,__FUNCTION__,"got UDP datagram with bad address family %d",fromaddr.sin_family);
		    else
		    {
			char tempa[32];
			char tempb[32];
			
			packet_set_size(upacket,len);
			
			if (fromaddr.sin_addr.s_addr==servaddr.sin_addr.s_addr) /* from server */
			{
			    if ((curr = list_get_first_const(virtconnlist()))) /* hack.. find proper client */
			    {
				vc = elem_get_data(curr);
				memset(&toaddr,0,sizeof(toaddr));
				toaddr.sin_family = PSOCK_AF_INET;
				toaddr.sin_port = htons(virtconn_get_udpport(vc));
				toaddr.sin_addr.s_addr = htonl(virtconn_get_udpaddr(vc));
				eventlog(eventlog_level_info,__FUNCTION__,"[%d] addr by UDP send is %s:%hu",virtconn_get_client_socket(vc),inet_ntoa(toaddr.sin_addr),ntohs(toaddr.sin_port));
				
				if (hexstrm)
				{
				    strcpy(tempa,inet_ntoa(fromaddr.sin_addr));
				    strcpy(tempb,inet_ntoa(toaddr.sin_addr));
				    fprintf(hexstrm,"%d: srv prot=UDP from=%s:%hu to=%s:%hu length=%d\n",
					    udpsock,
					    tempa,
					    ntohs(fromaddr.sin_port),
					    tempb,
					    ntohs(toaddr.sin_port),
					    len);
				    hexdump(hexstrm,packet_get_raw_data(upacket,0),len);
				}
				
				/*queue_push_packet(virtconn_get_clientout_queue(__));*/ /* where to queue ... */
				for (;;) /* hack.. just block for now */
				{
				    if (psock_sendto(udpsock,packet_get_raw_data_const(upacket,0),len,0,
					             (struct sockaddr *)&toaddr,(psock_t_socklen)sizeof(toaddr))<len)
				    {
					if (psock_errno()==PSOCK_EINTR || psock_errno()==PSOCK_EAGAIN || psock_errno()==PSOCK_EWOULDBLOCK)
					    continue;
					eventlog(eventlog_level_error,__FUNCTION__,"could not send UDP datagram to client (psock_sendto: %s)",pstrerror(psock_errno()));
				    }
				    break;
				}
			    }
			}
			else /* from client */
			{
			    if (hexstrm)
			    {
				strcpy(tempa,inet_ntoa(fromaddr.sin_addr));
				strcpy(tempb,inet_ntoa(servaddr.sin_addr));
				
				fprintf(hexstrm,"%d: clt prot=UDP from=%s:%hu to=%s:%hu length=%d\n",
					udpsock,
					tempa,
					ntohs(fromaddr.sin_port),
					tempb,
					ntohs(servaddr.sin_port),
					len);
				hexdump(hexstrm,packet_get_raw_data(upacket,0),len);
			    }
			    /*queue_push_packet(virtconn_get_serverout_queue(vc));*/
			    for (;;) /* hack.. just block for now */
			    {
				if (psock_sendto(udpsock,packet_get_raw_data_const(upacket,0),len,0,
					         (struct sockaddr *)&servaddr,(psock_t_socklen)sizeof(servaddr))<len)
				{
				    if (psock_errno()==PSOCK_EINTR || psock_errno()==PSOCK_EAGAIN || psock_errno()==PSOCK_EWOULDBLOCK)
					continue;
				    eventlog(eventlog_level_error,__FUNCTION__,"could not send UDP datagram to server (psock_sendto: %s)",pstrerror(psock_errno()));
				}
				break;
			    }
			}
		    }
		}
		packet_del_ref(upacket);
	    }
	}
	
	/* search connections for sockets that need service */
	eventlog(eventlog_level_debug,__FUNCTION__,"checking for sockets that need service");
	LIST_TRAVERSE_CONST(virtconnlist(),curr)
	{
	    unsigned int currsize;
	    t_packet *   packet;
	    
	    vc = elem_get_data(curr);
	    
            csocket = virtconn_get_client_socket(vc);
	    if (virtconn_get_state(vc)==virtconn_state_connected ||
		virtconn_get_state(vc)==virtconn_state_connecting)
		ssocket = virtconn_get_server_socket(vc);
	    else
		ssocket = -1;
	    
	    eventlog(eventlog_level_debug,__FUNCTION__,"checking %d for client readability",csocket);
	    if (PSOCK_FD_ISSET(csocket,&rfds))
	    {
		if (virtconn_get_state(vc)==virtconn_state_initial)
		{
		    if (init_virtconn(vc,servaddr)<0)
		    {
			virtconn_destroy(vc);
			continue;
		    }
		}
		else
		{
		    currsize = virtconn_get_clientin_size(vc);
		    
		    if (!queue_get_length(virtconn_get_clientin_queue(vc)))
		    {
			switch (virtconn_get_class(vc))
			{
			case virtconn_class_bnet:
			    if (!(packet = packet_create(packet_class_bnet)))
			    {
				eventlog(eventlog_level_error,__FUNCTION__,"could not allocate normal packet for input");
				continue;
			    }
			    break;
			case virtconn_class_file:
			    if (!(packet = packet_create(packet_class_file)))
			    {
				eventlog(eventlog_level_error,__FUNCTION__,"could not allocate file packet for input");
				continue;
			    }
			    break;
			case virtconn_class_bot:
			    if (!(packet = packet_create(packet_class_raw)))
			    {
				eventlog(eventlog_level_error,__FUNCTION__,"could not allocate raw packet for input");
				continue;
			    }
			    packet_set_size(packet,1); /* start by only reading one char */
			    break;
			default:
			    eventlog(eventlog_level_error,__FUNCTION__,"[%d] connection has bad type (closing connection)",virtconn_get_client_socket(vc));
			    virtconn_destroy(vc);
			    continue;
			}
			queue_push_packet(virtconn_get_clientin_queue(vc),packet);
			packet_del_ref(packet);
			if (!queue_get_length(virtconn_get_clientin_queue(vc)))
			    continue; /* push failed */
			currsize = 0;
		    }
		    
		    packet = queue_peek_packet((t_queue const * const *)virtconn_get_clientin_queue(vc)); /* avoid warning */
		    switch (net_recv_packet(csocket,packet,&currsize))
		    {
		    case -1:
			virtconn_destroy(vc);
			continue;
			
		    case 0: /* still working on it */
			virtconn_set_clientin_size(vc,currsize);
			break;
			
		    case 1: /* done reading */
			if (virtconn_get_class(vc)==virtconn_class_bot &&
			    currsize<MAX_PACKET_SIZE)
			{
			    char const * const temp=packet_get_raw_data_const(packet,0);
			    
			    if (temp[currsize-1]!='\r' && temp[currsize-1]!='\n')
			    {
				virtconn_set_clientin_size(vc,currsize);
				packet_set_size(packet,currsize+1);
			        break; /* no end of line, get another char */
			    }
			    /* got a complete line... fall through */
			}
			
			packet = queue_pull_packet(virtconn_get_clientin_queue(vc));
			
			if (hexstrm)
			{
			    fprintf(hexstrm,"%d: cli class=%s[0x%04hx] type=%s[0x%04hx] length=%hu\n",
				    csocket,
				    packet_get_class_str(packet),packet_get_class(packet),
				    packet_get_type_str(packet,packet_dir_from_client),packet_get_type(packet),
				    packet_get_size(packet));
			    hexdump(hexstrm,packet_get_raw_data_const(packet,0),packet_get_size(packet));
			}
			
			queue_push_packet(virtconn_get_serverout_queue(vc),packet);
			packet_del_ref(packet);
			virtconn_set_clientin_size(vc,0);
		    }
		}
	    }
	    
	    eventlog(eventlog_level_debug,__FUNCTION__,"checking %d for server readability",ssocket);
	    if (ssocket!=-1 && PSOCK_FD_ISSET(ssocket,&rfds))
	    {
		currsize = virtconn_get_serverin_size(vc);
		
		if (!queue_get_length(virtconn_get_serverin_queue(vc)))
		{
		    switch (virtconn_get_class(vc))
		    {
		    case virtconn_class_bnet:
			if (!(packet = packet_create(packet_class_bnet)))
			{
			    eventlog(eventlog_level_error,__FUNCTION__,"could not allocate normal packet for input");
			    continue;
			}
			break;
		    case virtconn_class_file:
			{
			    unsigned int fileleft;
			    
			    if ((fileleft = virtconn_get_fileleft(vc))>0)
			    {
				if (!(packet = packet_create(packet_class_raw)))
				{
				    eventlog(eventlog_level_error,__FUNCTION__,"could not allocate raw file packet for input");
				    continue;
				}
				if (fileleft>MAX_PACKET_SIZE)
				    packet_set_size(packet,MAX_PACKET_SIZE);
				else
				    packet_set_size(packet,fileleft);
			    }
			    else
			    {
				if (!(packet = packet_create(packet_class_file)))
				{
				    eventlog(eventlog_level_error,__FUNCTION__,"could not allocate file packet for input");
				    continue;
				}
			    }
			}
			break;
		    case virtconn_class_bot:
			if (!(packet = packet_create(packet_class_raw)))
			{
			    eventlog(eventlog_level_error,__FUNCTION__,"could not allocate raw packet for input");
			    continue;
			}
			packet_set_size(packet,MAX_PACKET_SIZE); /* read as much as possible */
			break;
		    default:
			eventlog(eventlog_level_error,__FUNCTION__,"[%d] connection has bad type (closing connection)",virtconn_get_client_socket(vc));
			virtconn_destroy(vc);
			continue;
		    }
		    queue_push_packet(virtconn_get_serverin_queue(vc),packet);
		    packet_del_ref(packet);
		    if (!queue_get_length(virtconn_get_serverin_queue(vc)))
			continue; /* push failed */
		    currsize = 0;
		}
		
		packet = queue_peek_packet((t_queue const * const *)virtconn_get_serverin_queue(vc)); /* avoid warning */
		switch (net_recv_packet(ssocket,packet,&currsize))
		{
		case -1:
		    virtconn_destroy(vc);
		    continue;
		    
		case 0: /* still working on it */
		    virtconn_set_serverin_size(vc,currsize);
		    if (virtconn_get_class(vc)!=virtconn_class_bot || currsize<1)
			break;
		    else
			packet_set_size(packet,currsize);
		    /* fallthough... we take what we can get with the bot data */
		    
		case 1: /* done reading */
		    packet = queue_pull_packet(virtconn_get_serverin_queue(vc));
		    if (virtconn_get_class(vc)==virtconn_class_file)
		    {
			unsigned int len=virtconn_get_fileleft(vc);
			
			if (len)
			    virtconn_set_fileleft(vc,len-currsize);
			else if (packet_get_type(packet)==SERVER_FILE_REPLY &&
				 packet_get_size(packet)>=sizeof(t_server_file_reply))
			    virtconn_set_fileleft(vc,bn_int_get(packet->u.server_file_reply.filelen));
		    }
		    queue_push_packet(virtconn_get_clientout_queue(vc),packet);
		    packet_del_ref(packet);
		    virtconn_set_serverin_size(vc,0);
		}
	    }
	    
	    eventlog(eventlog_level_debug,__FUNCTION__,"checking %d for client writeability",csocket);
	    if (PSOCK_FD_ISSET(csocket,&wfds))
	    {
		currsize = virtconn_get_clientout_size(vc);
		switch (net_send_packet(csocket,queue_peek_packet((t_queue const * const *)virtconn_get_clientout_queue(vc)),&currsize)) /* avoid warning */
		{
		case -1:
		    virtconn_destroy(vc);
		    continue;
		    
		case 0: /* still working on it */
		    virtconn_set_clientout_size(vc,currsize);
		    break;
		    
		case 1: /* done sending */
		    packet = queue_pull_packet(virtconn_get_clientout_queue(vc));
		    
		    if (hexstrm)
		    {
			fprintf(hexstrm,"%d: srv class=%s[0x%04hx] type=%s[0x%04hx] length=%hu\n",
				csocket,
				packet_get_class_str(packet),packet_get_class(packet),
				packet_get_type_str(packet,packet_dir_from_server),packet_get_type(packet),
				packet_get_size(packet));
			hexdump(hexstrm,packet_get_raw_data(packet,0),packet_get_size(packet));
		    }
		    
		    packet_del_ref(packet);
		    virtconn_set_clientout_size(vc,0);
		}
	    }
	    
	    eventlog(eventlog_level_debug,__FUNCTION__,"checking %d for server writeability",ssocket);
	    if (ssocket!=-1 && PSOCK_FD_ISSET(ssocket,&wfds))
	    {
		if (virtconn_get_state(vc)==virtconn_state_connecting)
		{
		    int             err;
		    psock_t_socklen errlen;
		    
		    err = 0;
		    errlen = sizeof(err);
		    if (psock_getsockopt(ssocket,PSOCK_SOL_SOCKET,PSOCK_SO_ERROR,&err,&errlen)<0)
		    {
			eventlog(eventlog_level_error,__FUNCTION__,"[%d] unable to read socket error (psock_getsockopt[psock_connect]: %s)",virtconn_get_client_socket(vc),pstrerror(psock_errno()));
			virtconn_destroy(vc);
			continue;
		    }
		    if (errlen==0 || err==0)
			virtconn_set_state(vc,virtconn_state_connected);
		    else
		    {
			eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not connect to server (psock_getsockopt[psock_connect]: %s)",virtconn_get_client_socket(vc),pstrerror(err));
			virtconn_destroy(vc);
			continue;
		    }
		}
		else
		{
		    currsize = virtconn_get_serverout_size(vc);
		    switch (net_send_packet(ssocket,queue_peek_packet((t_queue const * const *)virtconn_get_serverout_queue(vc)),&currsize)) /* avoid warning */
		    {
		    case -1:
			virtconn_destroy(vc);
			continue;
			
		    case 0: /* still working on it */
			virtconn_set_serverout_size(vc,currsize);
			break;
			
		    case 1: /* done sending */
			packet = queue_pull_packet(virtconn_get_serverout_queue(vc));
			packet_del_ref(packet);
			virtconn_set_serverout_size(vc,0);
		    }
		}
	    }
	}
示例#11
0
extern int server_process(void)
{
    t_addrlist *    laddrs;
    t_addr *        curr_laddr;
    t_addr_data     laddr_data;
    t_laddr_info *  laddr_info;
#ifdef HAVE_POLL
    struct pollfd * fds = NULL;
    int             num_fd;
#else
    struct timeval  tv;
    t_psock_fd_set  rfds, wfds;
    int             highest_fd;
#endif
    time_t          curr_exittime, prev_exittime, prev_savetime, track_time, now;
    unsigned int    syncdelta;
    t_connection *  c;
    t_elem const *  acurr;
    t_elem const *  ccurr;
    int             csocket;
#ifdef DO_POSIXSIG
    sigset_t        block_set;
    sigset_t        save_set;
#endif
    unsigned int    count;

    if (psock_init()<0)
    {
	eventlog(eventlog_level_error,"server_process","could not initialize socket functions");
	return -1;
    }

    server_set_name();

    /* Start with the Battle.net address list */
    if (!(laddrs = addrlist_create(prefs_get_bnetdserv_addrs(),INADDR_ANY,BNETD_SERV_PORT)))
    {
	eventlog(eventlog_level_error,"server_process","could not create %s server address list from \"%s\"",laddr_type_get_str(laddr_type_bnet),prefs_get_bnetdserv_addrs());
        if (psock_deinit()<0)
	    eventlog(eventlog_level_error,"server_process","could not deinitialize socket functions");
	return -1;
    }
    /* Mark all these laddrs for being classic Battle.net service */
    LIST_TRAVERSE_CONST(laddrs,acurr)
    {
	curr_laddr = elem_get_data(acurr);
	if (addr_get_data(curr_laddr).p)
	    continue;
	if (!(laddr_info = malloc(sizeof(t_laddr_info))))
	{
	    eventlog(eventlog_level_error,"server_process","could not create %s address info (malloc: %s)",laddr_type_get_str(laddr_type_bnet),strerror(psock_errno()));
	    goto error_addr_list;
	}
        laddr_info->usocket = -1;
        laddr_info->ssocket = -1;
        laddr_info->type = laddr_type_bnet;
	laddr_data.p = laddr_info;
        if (addr_set_data(curr_laddr,laddr_data)<0)
	{
	    eventlog(eventlog_level_error,"server_process","could not set address data");
	    if (laddr_info->usocket!=-1)
	    {
		psock_close(laddr_info->usocket);
		laddr_info->usocket = -1;
	    }
	    if (laddr_info->ssocket!=-1)
	    {
		psock_close(laddr_info->ssocket);
		laddr_info->ssocket = -1;
	    }
	    goto error_poll;
	}
    }
示例#12
0
static int sd_accept(t_addr const * curr_laddr, t_laddr_info const * laddr_info, int ssocket, int usocket)
{
    char               tempa[32];
    int                csocket;
    struct sockaddr_in caddr;
    psock_t_socklen    caddr_len;
    unsigned int       raddr;
    unsigned short     rport;

    if (!addr_get_addr_str(curr_laddr,tempa,sizeof(tempa)))
	strcpy(tempa,"x.x.x.x:x");

    /* accept the connection */
    memset(&caddr,0,sizeof(caddr)); /* not sure if this is needed... modern systems are ok anyway */
    caddr_len = sizeof(caddr);
    if ((csocket = psock_accept(ssocket,(struct sockaddr *)&caddr,&caddr_len))<0)
    {
	/* BSD, POSIX error for aborted connections, SYSV often uses EAGAIN or EPROTO */
	if (
#ifdef PSOCK_EWOULDBLOCK
	    psock_errno()==PSOCK_EWOULDBLOCK ||
#endif
#ifdef PSOCK_ECONNABORTED
	    psock_errno()==PSOCK_ECONNABORTED ||
#endif
#ifdef PSOCK_EPROTO
	    psock_errno()==PSOCK_EPROTO ||
#endif
	    0)
	    eventlog(eventlog_level_error,"sd_accept","client aborted connection on %s (psock_accept: %s)",tempa,strerror(psock_errno()));
	else /* EAGAIN can mean out of resources _or_ connection aborted :( */
	    if (
#ifdef PSOCK_EINTR
		psock_errno()!=PSOCK_EINTR &&
#endif
		1)
		eventlog(eventlog_level_error,"sd_accept","could not accept new connection on %s (psock_accept: %s)",tempa,strerror(psock_errno()));
	return -1;
    }

#ifdef HAVE_POLL
    if (csocket>=BNETD_MAX_SOCKETS) /* This check is a bit too strict (csocket is probably
                                     * greater than the number of connections) but this makes
                                     * life easier later.
                                     */
    {
	eventlog(eventlog_level_error,"sd_accept","csocket is beyond range allowed by BNETD_MAX_SOCKETS for poll() (%d>=%d)",csocket,BNETD_MAX_SOCKETS);
	psock_close(csocket);
	return -1;
    }
#else
# ifdef FD_SETSIZE
    if (csocket>=FD_SETSIZE) /* fd_set size is determined at compile time */
    {
	eventlog(eventlog_level_error,"sd_accept","csocket is beyond range allowed by FD_SETSIZE for select() (%d>=%d)",csocket,FD_SETSIZE);
	psock_close(csocket);
	return -1;
    }
# endif
#endif
    if (ipbanlist_check(inet_ntoa(caddr.sin_addr))!=0)
    {
	eventlog(eventlog_level_info,"sd_accept","[%d] connection from banned address %s denied (closing connection)",csocket,inet_ntoa(caddr.sin_addr));
	psock_close(csocket);
	return -1;
    }

    eventlog(eventlog_level_info,"sd_accept","[%d] accepted connection from %s on %s",csocket,addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr),ntohs(caddr.sin_port)),tempa);

    if (prefs_get_use_keepalive())
    {
	int val=1;

	if (psock_setsockopt(csocket,PSOCK_SOL_SOCKET,PSOCK_SO_KEEPALIVE,&val,(psock_t_socklen)sizeof(val))<0)
	    eventlog(eventlog_level_error,"sd_accept","[%d] could not set socket option SO_KEEPALIVE (psock_setsockopt: %s)",csocket,strerror(psock_errno()));
	/* not a fatal error */
    }

    {
	struct sockaddr_in rsaddr;
	psock_t_socklen    rlen;

	memset(&rsaddr,0,sizeof(rsaddr)); /* not sure if this is needed... modern systems are ok anyway */
	rlen = sizeof(rsaddr);
	if (psock_getsockname(csocket,(struct sockaddr *)&rsaddr,&rlen)<0)
	{
	    eventlog(eventlog_level_error,"sd_accept","[%d] unable to determine real local port (psock_getsockname: %s)",csocket,strerror(psock_errno()));
	    /* not a fatal error */
	    raddr = addr_get_ip(curr_laddr);
	    rport = addr_get_port(curr_laddr);
	}
	else
	{
	    if (rsaddr.sin_family!=PSOCK_AF_INET)
	    {
		eventlog(eventlog_level_error,"sd_accept","local address returned with bad address family %d",(int)rsaddr.sin_family);
		/* not a fatal error */
		raddr = addr_get_ip(curr_laddr);
		rport = addr_get_port(curr_laddr);
	    }
	    else
	    {
		raddr = ntohl(rsaddr.sin_addr.s_addr);
		rport = ntohs(rsaddr.sin_port);
	    }
	}
    }

    if (psock_ctl(csocket,PSOCK_NONBLOCK)<0)
    {
	eventlog(eventlog_level_error,"sd_accept","[%d] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: %s)",csocket,strerror(psock_errno()));
	psock_close(csocket);
	return -1;
    }

    {
	t_connection * c;

	if (!(c = conn_create(csocket,usocket,raddr,rport,addr_get_ip(curr_laddr),addr_get_port(curr_laddr),ntohl(caddr.sin_addr.s_addr),ntohs(caddr.sin_port))))
	{
	    eventlog(eventlog_level_error,"sd_accept","[%d] unable to create new connection (closing connection)",csocket);
	    psock_close(csocket);
	    return -1;
	}

	eventlog(eventlog_level_debug,"sd_accept","[%d] client connected to a %s listening address",csocket,laddr_type_get_str(laddr_info->type));
	switch (laddr_info->type)
	{
	case laddr_type_irc:
	    conn_set_class(c,conn_class_irc);
	    conn_set_state(c,conn_state_connected);
	    break;
	case laddr_type_telnet:
	    conn_set_class(c,conn_class_telnet);
	    conn_set_state(c,conn_state_connected);
	    break;
	case laddr_type_bnet:
	default:
	    /* We have to wait for an initial "magic" byte on bnet connections to
             * tell us exactly what connection class we are dealing with.
             */
	    break;
	}
    }

    return 0;
}
示例#13
0
FAR char *telnetd_driver(int sd, FAR struct telnetd_s *daemon)
{
  FAR struct telnetd_dev_s *priv;
  FAR struct socket *psock;
  FAR char *devpath = NULL;
  int ret;

  /* Allocate instance data for this driver */

  priv = (FAR struct telnetd_dev_s*)malloc(sizeof(struct telnetd_dev_s));
  if (!priv)
    {
      nlldbg("Failed to allocate the driver data structure\n");
      return NULL;
    }

  /* Initialize the allocated driver instance */

  sem_init(&priv->td_exclsem, 0, 1);

  priv->td_state   = STATE_NORMAL;
  priv->td_crefs   = 0;
  priv->td_pending = 0;
  priv->td_offset  = 0;

  /* Clone the internal socket structure.  We do this so that it will be
   * independent of threads and of socket descriptors (the original socket
   * instance resided in the daemon's socket array).
   */

  psock = sockfd_socket(sd);
  if (!psock)
    {
      nlldbg("Failed to convert sd=%d to a socket structure\n", sd);
      goto errout_with_dev;
    }

  ret = net_clone(psock, &priv->td_psock);
  if (ret < 0)
    {
      nlldbg("net_clone failed: %d\n", ret);
      goto errout_with_dev;
    }

  /* And close the original */

  psock_close(psock);

  /* Allocation a unique minor device number of the telnet drvier */

  do
    {
      ret = sem_wait(&g_telnetdcommon.exclsem);
      if (ret < 0 && errno != -EINTR)
        {
          goto errout_with_dev;
        }
    }
  while (ret < 0);

  priv->td_minor = g_telnetdcommon.minor;
  g_telnetdcommon.minor++;
  sem_post(&g_telnetdcommon.exclsem);

  /* Create a path and name for the driver. */

  ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor);
  if (ret < 0)
    {
      nlldbg("Failed to allocate the driver path\n");
      goto errout_with_dev;
    }

  /* Register the driver */

  ret = register_driver(devpath, &g_telnetdfops, 0666, priv);
  if (ret < 0)
    {
      nlldbg("Failed to register the driver %s: %d\n", devpath, ret);
      goto errout_with_devpath;
    }

  /* Return the path to the new telnet driver */

  return devpath;

errout_with_devpath:
  free(devpath);
errout_with_dev:
  free(priv);
  return NULL;
}
示例#14
0
static int telnetd_close(FAR struct file *filep)
{
  FAR struct inode *inode = filep->f_inode;
  FAR struct telnetd_dev_s *priv = inode->i_private;
  FAR char *devpath;
  int ret;

  nllvdbg("td_crefs: %d\n", priv->td_crefs);

  /* Get exclusive access to the device structures */

  ret = sem_wait(&priv->td_exclsem);
  if (ret < 0)
    {
      ret = -errno;
      goto errout;
    }

  /* Decrement the references to the driver.  If the reference count will
   * decrement to 0, then uninitialize the driver.
   */

  if (priv->td_crefs > 1)
    {
      /* Just decrement the reference count and release the semaphore */

      priv->td_crefs--;
      sem_post(&priv->td_exclsem);
    }
  else
    {
      /* Re-create the path to the driver. */

      sched_lock();
      ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor);
      if (ret < 0)
        {
          nlldbg("Failed to allocate the driver path\n");
        }
      else
        {
          /* Un-register the character driver */

          ret = unregister_driver(devpath);
          if (ret < 0)
            {
              /* NOTE: a return value of -EBUSY is not an error, it simply
               * means that the Telnet driver is busy now and cannot be
               * registered now because there are other sessions using the
               * connection.  The driver will be properly unregistered when
               * the final session terminates.
               */

              nlldbg("Failed to unregister the driver %s: %d\n",
                     devpath, ret);
            }

          free(devpath);
        }

      /* Close the socket */

      psock_close(&priv->td_psock);

      /* Release the driver memory.  What if there are threads waiting on
       * td_exclsem?  They will never be awakened!  How could this happen?
       * crefs == 1 so there are no other open references to the driver.
       * But this could have if someone were trying to re-open the driver
       * after every other thread has closed it.  That really should not
       * happen in the intended usage model.
       */

      DEBUGASSERT(priv->td_exclsem.semcount == 0);
      sem_destroy(&priv->td_exclsem);
      free(priv);
      sched_unlock();
    }

  ret = OK;

errout:
  return ret;
}