static int on_d2cs_authreply(t_connection * c, t_packet const * packet) { t_packet * rpacket; unsigned int version; unsigned int try_version; unsigned int reply; char const * realmname; t_realm * realm; if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_authreply)) { eventlog(eventlog_level_error,"on_d2cs_authreply","got bad packet size"); return -1; } if (!(realmname=packet_get_str_const(packet,sizeof(t_d2cs_bnetd_authreply),REALM_NAME_LEN))) { eventlog(eventlog_level_error,"on_d2cs_authreply","got bad realmname"); return -1; } if (!(realm=realmlist_find_realm_by_ip(conn_get_addr(c)))) { eventlog(eventlog_level_error,"handle_init_packet", "realm not found"); return -1; } if (realm_get_name(realm) && strcasecmp(realmname,realm_get_name(realm))) { eventlog(eventlog_level_error,"handle_init_packet", "warn: realm name mismatch %s %s", realm_get_name(realm),realmname); } version=prefs_get_d2cs_version(); try_version=bn_int_get(packet->u.d2cs_bnetd_authreply.version); if (version && version != try_version) { eventlog(eventlog_level_error,"on_d2cs_authreply","d2cs version mismatch 0x%X - 0x%X", try_version,version); reply=BNETD_D2CS_AUTHREPLY_BAD_VERSION; } else { reply=BNETD_D2CS_AUTHREPLY_SUCCEED; } if (reply==BNETD_D2CS_AUTHREPLY_SUCCEED) { eventlog(eventlog_level_error,"on_d2cs_authreply","d2cs %s authed", addr_num_to_ip_str(conn_get_addr(c))); conn_set_state(c,conn_state_loggedin); if (prefs_allow_d2cs_setname()) realm_set_name(realm,realmname); realm_active(realm,c); } else { eventlog(eventlog_level_error,"on_d2cs_authreply","failed to auth d2cs %s", addr_num_to_ip_str(conn_get_addr(c))); } if ((rpacket=packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket,sizeof(t_bnetd_d2cs_authreply)); packet_set_type(rpacket,BNETD_D2CS_AUTHREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_authreply.reply,reply); queue_push_packet(conn_get_out_queue(c),rpacket); packet_del_ref(rpacket); } return 0; }
extern int handle_d2cs_init(t_connection * c) { t_packet * packet; if ((packet=packet_create(packet_class_d2cs_bnetd))) { packet_set_size(packet,sizeof(t_bnetd_d2cs_authreq)); packet_set_type(packet,BNETD_D2CS_AUTHREQ); bn_int_set(&packet->u.bnetd_d2cs_authreq.sessionnum,conn_get_sessionnum(c)); queue_push_packet(conn_get_out_queue(c),packet); packet_del_ref(packet); } eventlog(eventlog_level_info,"handle_d2cs_init","sent init packet to d2cs (sessionnum=%d)", conn_get_sessionnum(c)); return 0; }
static int on_d2cs_charloginreq(t_connection * c, t_packet const * packet) { t_connection * client; char const * charname; char const * portrait; char const * clienttag; char * temp; unsigned int sessionnum; char const * tname; char const * realmname; unsigned int pos, reply; t_packet * rpacket; if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_charloginreq)) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","got bad packet size"); return -1; } sessionnum=bn_int_get(packet->u.d2cs_bnetd_charloginreq.sessionnum); pos=sizeof(t_d2cs_bnetd_charloginreq); if (!(charname=packet_get_str_const(packet,pos,CHAR_NAME_LEN))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","got bad character name"); return -1; } pos+=strlen(charname)+1; if (!(portrait=packet_get_str_const(packet,pos,CHAR_PORTRAIT_LEN))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","got bad character portrait"); return -1; } if (!(client=connlist_find_connection_by_sessionnum(sessionnum))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","user %d not found",sessionnum); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else if (!(clienttag=conn_get_clienttag(client))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","got NULL clienttag"); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else if (!(realmname=conn_get_realmname(client))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","got NULL realm name"); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else if (!(temp=malloc(strlen(clienttag)+strlen(realmname)+1+strlen(charname)+1+\ strlen(portrait)+1))) { eventlog(eventlog_level_error,"on_d2cs_charloginreq","error allocate temp"); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else { reply = BNETD_D2CS_CHARLOGINREPLY_SUCCEED; sprintf (temp,"PX2D%s,%s,%s",realmname,charname,portrait); bn_int_tag_set((bn_int *)temp,clienttag); conn_set_charname(client,charname); conn_set_realminfo(client,temp); free(temp); eventlog(eventlog_level_debug,"on_d2cs_charloginreq",\ "loaded portrait for character %s",charname); } if ((rpacket=packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket,sizeof(t_bnetd_d2cs_charloginreply)); packet_set_type(rpacket,BNETD_D2CS_CHARLOGINREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.h.seqno,\ bn_int_get(packet->u.d2cs_bnetd_charloginreq.h.seqno)); bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.reply,reply); queue_push_packet(conn_get_out_queue(c),rpacket); packet_del_ref(rpacket); } return 0; }
static int on_d2cs_accountloginreq(t_connection * c, t_packet const * packet) { unsigned int sessionkey; unsigned int sessionnum; unsigned int salt; char const * account; char const * tname; t_connection * client; int reply; t_packet * rpacket; struct { bn_int salt; bn_int sessionkey; bn_int sessionnum; bn_int secret; bn_int passhash[5]; } temp; t_hash secret_hash; char const * pass_str; t_hash passhash; t_hash try_hash; if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_accountloginreq)) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","got bad packet size"); return -1; } if (!(account=packet_get_str_const(packet,sizeof(t_d2cs_bnetd_accountloginreq),USER_NAME_MAX))) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","got bad account name"); return -1; } sessionkey=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionkey); sessionnum=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionnum); salt=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.seqno); if (!(client=connlist_find_connection_by_sessionnum(sessionnum))) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","sessionnum %d not found",sessionnum); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (sessionkey!=conn_get_sessionkey(client)) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","sessionkey %d not match",sessionkey); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (!(tname=conn_get_username(client))) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","got NULL username"); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (strcasecmp(account,tname)) { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","username %s not match",account); conn_unget_username(client,tname); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else { conn_unget_username(client,tname); bn_int_set(&temp.salt,salt); bn_int_set(&temp.sessionkey,sessionkey); bn_int_set(&temp.sessionnum,sessionnum); bn_int_set(&temp.secret,conn_get_secret(client)); pass_str=account_get_pass(conn_get_account(client)); if (hash_set_str(&passhash,pass_str)<0) { reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else { hash_to_bnhash((t_hash const *)&passhash,temp.passhash); bnet_hash(&secret_hash,sizeof(temp),&temp); bnhash_to_hash(packet->u.d2cs_bnetd_accountloginreq.secret_hash,&try_hash); if (hash_eq(try_hash,secret_hash)==1) { eventlog(eventlog_level_debug,"on_d2cs_accountloginreq","user %s loggedin on d2cs",\ account); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_SUCCEED; } else { eventlog(eventlog_level_error,"on_d2cs_accountloginreq","user %s hash not match",\ account); reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } } account_unget_pass(pass_str); } if ((rpacket=packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket,sizeof(t_bnetd_d2cs_accountloginreply)); packet_set_type(rpacket,BNETD_D2CS_ACCOUNTLOGINREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.h.seqno,\ bn_int_get(packet->u.d2cs_bnetd_accountloginreq.h.seqno)); bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.reply,reply); queue_push_packet(conn_get_out_queue(c),rpacket); packet_del_ref(rpacket); } return 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); } } } }
static int init_virtconn(t_virtconn * vc, struct sockaddr_in servaddr) { int addlen; char connect_type; /* determine connection type by first character sent by client */ addlen = psock_recv(virtconn_get_client_socket(vc),&connect_type,sizeof(char),0); if (addlen<0 && (psock_errno()==PSOCK_EINTR || psock_errno()==PSOCK_EAGAIN || psock_errno()==PSOCK_EWOULDBLOCK)) return 0; /* error occurred or connection lost */ if (addlen<1) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not get virtconn class (closing connection) (psock_recv: %s)",virtconn_get_client_socket(vc),pstrerror(psock_errno())); return -1; } switch (connect_type) { case CLIENT_INITCONN_CLASS_BNET: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated normal connection",virtconn_get_client_socket(vc)); virtconn_set_class(vc,virtconn_class_bnet); break; case CLIENT_INITCONN_CLASS_FILE: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated file download connection",virtconn_get_client_socket(vc)); virtconn_set_class(vc,virtconn_class_file); break; case CLIENT_INITCONN_CLASS_BOT: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated chat bot connection",virtconn_get_client_socket(vc)); virtconn_set_class(vc,virtconn_class_bot); break; default: eventlog(eventlog_level_error,__FUNCTION__,"[%d] client initiated unknown connection type 0x%02hx (length %d) (closing connection)",virtconn_get_client_socket(vc),(unsigned short)connect_type,addlen); return -1; } /* now connect to the real server */ if (psock_connect(virtconn_get_server_socket(vc),(struct sockaddr *)&servaddr,(psock_t_socklen)sizeof(servaddr))<0) { if (psock_errno()!=PSOCK_EINPROGRESS) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not connect to server (psock_connect: %s)\n",virtconn_get_client_socket(vc),pstrerror(psock_errno())); return -1; } virtconn_set_state(vc,virtconn_state_connecting); } else virtconn_set_state(vc,virtconn_state_connected); { t_packet * packet; if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] could not create packet",virtconn_get_client_socket(vc)); return -1; } packet_append_data(packet,&connect_type,1); queue_push_packet(virtconn_get_serverout_queue(vc),packet); packet_del_ref(packet); } return 0; }
static int sd_tcpinput(int csocket, t_connection * c) { unsigned int currsize; t_packet * packet; currsize = conn_get_in_size(c); if (!*conn_get_in_queue(c)) { switch (conn_get_class(c)) { case conn_class_init: if (!(packet = packet_create(packet_class_init))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate init packet for input"); return -1; } break; case conn_class_d2cs_bnetd: if (!(packet = packet_create(packet_class_d2cs_bnetd))) { eventlog(eventlog_level_error,"server_process","could not allocate d2cs_bnetd packet"); return -1; } break; case conn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate bnet packet for input"); return -1; } break; case conn_class_file: if (!(packet = packet_create(packet_class_file))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate file packet for input"); return -1; } break; case conn_class_bits: if (!(packet = packet_create(packet_class_bits))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate BITS packet for input"); return -1; } break; case conn_class_defer: case conn_class_bot: case conn_class_irc: case conn_class_telnet: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate raw packet for input"); return -1; } packet_set_size(packet,1); /* start by only reading one char */ break; case conn_class_auth: if (!(packet = packet_create(packet_class_auth))) { eventlog(eventlog_level_error,"sd_tcpinput","could not allocate auth packet for input"); return -1; } break; default: eventlog(eventlog_level_error,"sd_tcpinput","[%d] connection has bad class (closing connection)",conn_get_socket(c)); conn_destroy(c); return -1; } queue_push_packet(conn_get_in_queue(c),packet); packet_del_ref(packet); if (!*conn_get_in_queue(c)) return -1; /* push failed */ currsize = 0; } packet = queue_peek_packet((t_queue const * const *)conn_get_in_queue(c)); /* avoid warning */ switch (net_recv_packet(csocket,packet,&currsize)) { case -1: eventlog(eventlog_level_debug,"sd_tcpinput","[%d] read FAILED (closing connection)",conn_get_socket(c)); conn_destroy(c); return -1; case 0: /* still working on it */ /* eventlog(eventlog_level_debug,"sd_tcpinput","[%d] still reading \"%s\" packet (%u of %u bytes so far)",conn_get_socket(c),packet_get_class_str(packet),conn_get_in_size(c),packet_get_size(packet)); */ conn_set_in_size(c,currsize); break; case 1: /* done reading */ switch (conn_get_class(c)) { case conn_class_defer: { unsigned char const * const temp=packet_get_raw_data_const(packet,0); eventlog(eventlog_level_debug,"sd_tcpinput","[%d] got first packet byte %02x",conn_get_socket(c),(unsigned int)temp[0]); if (temp[0]==(unsigned char)0xff) /* HACK: thankfully all bnet packet types end with ff */ { conn_set_class(c,conn_class_bnet); conn_set_in_size(c,currsize); packet_set_class(packet,packet_class_bnet); eventlog(eventlog_level_debug,"sd_tcpinput","[%d] defered connection class is bnet",conn_get_socket(c)); } else { conn_set_class(c,conn_class_auth); conn_set_in_size(c,currsize); packet_set_class(packet,packet_class_auth); eventlog(eventlog_level_debug,"sd_tcpinput","[%d] defered connection class is auth",conn_get_socket(c)); } } break; case conn_class_bot: case conn_class_telnet: if (currsize<MAX_PACKET_SIZE) /* if we overflow, we can't wait for the end of the line. handle_*_packet() should take care of it */ { char const * const temp=packet_get_raw_data_const(packet,0); if ((temp[currsize-1]=='\003')||(temp[currsize-1]=='\004')) { /* we have to ignore these special characters, since * some bots even send them after login (eg. UltimateBot) */ currsize--; break; } if (temp[currsize-1]!='\r' && temp[currsize-1]!='\n') { conn_set_in_size(c,currsize); packet_set_size(packet,currsize+1); break; /* no end of line, get another char */ } } /* got a complete line or overflow, so: */ /*FALLTHRU*/ default: packet = queue_pull_packet(conn_get_in_queue(c)); if (hexstrm) { fprintf(hexstrm,"%d: recv class=%s[0x%02x] type=%s[0x%04x] length=%u\n", csocket, packet_get_class_str(packet),(unsigned int)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)); } if (conn_get_class(c)==conn_class_bot || conn_get_class(c)==conn_class_telnet) /* NUL terminate the line to make life easier */ { char * const temp=packet_get_raw_data(packet,0); if (temp[currsize-1]=='\r' || temp[currsize-1]=='\n') temp[currsize-1] = '\0'; /* have to do it here instead of above so everything is intact for the hexdump */ } { int ret; switch (conn_get_class(c)) { case conn_class_bits: #ifdef WITH_BITS ret = handle_bits_packet(c,packet); #else eventlog(eventlog_level_error,"sd_tcpinput","[%d] BITS not enabled (closing connection)",conn_get_socket(c)); ret = -1; #endif break; case conn_class_init: ret = handle_init_packet(c,packet); break; case conn_class_bnet: ret = handle_bnet_packet(c,packet); break; case conn_class_d2cs_bnetd: ret = handle_d2cs_packet(c,packet); break; case conn_class_bot: ret = handle_bot_packet(c,packet); break; case conn_class_telnet: ret = handle_telnet_packet(c,packet); break; case conn_class_file: ret = handle_file_packet(c,packet); break; case conn_class_auth: ret = handle_auth_packet(c,packet); break; case conn_class_irc: ret = handle_irc_packet(c,packet); break; default: eventlog(eventlog_level_error,"sd_tcpinput","[%d] bad packet class %d (closing connection)",conn_get_socket(c),(int)packet_get_class(packet)); ret = -1; } packet_del_ref(packet); if (ret<0) { conn_destroy(c); return -1; } } conn_set_in_size(c,0); } } return 0; }