extern int handle_d2cs_packet(t_connection * c, t_packet const * packet) { if (!c) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection"); return -1; } if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet"); return -1; } if (packet_get_class(packet)!=packet_class_d2cs_bnetd) { eventlog(eventlog_level_error,__FUNCTION__,"got bad packet class %d", packet_get_class(packet)); return -1; } switch (conn_get_state(c)) { case conn_state_connected: switch (packet_get_type(packet)) { case D2CS_BNETD_AUTHREPLY: on_d2cs_authreply(c,packet); break; default: eventlog(eventlog_level_error,__FUNCTION__, "got unknown packet type %d",packet_get_type(packet)); break; } break; case conn_state_loggedin: switch (packet_get_type(packet)) { case D2CS_BNETD_ACCOUNTLOGINREQ: on_d2cs_accountloginreq(c,packet); break; case D2CS_BNETD_CHARLOGINREQ: on_d2cs_charloginreq(c,packet); break; case D2CS_BNETD_GAMEINFOREPLY: on_d2cs_gameinforeply(c,packet); break; default: eventlog(eventlog_level_error,__FUNCTION__, "got unknown packet type %d",packet_get_type(packet)); break; } break; default: eventlog(eventlog_level_error,__FUNCTION__, "got unknown connection state %d",conn_get_state(c)); break; } return 0; }
/*! * @brief Call the dispatch routine for a given command. * @param command The command to call the dispatch routine on. * @param remote Pointer to the remote connection. * @param packet Pointer to the current packet. * @return Result of the command dispatch handler call. */ DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet) { DWORD res; // Validate the arguments, if requested. Always make sure argument // lengths are sane. if ((res = command_validate_arguments(command, packet)) != ERROR_SUCCESS) return res; switch (packet_get_type(packet)) { case PACKET_TLV_TYPE_REQUEST: case PACKET_TLV_TYPE_PLAIN_REQUEST: if (command->request.handler) res = command->request.handler(remote, packet); break; case PACKET_TLV_TYPE_RESPONSE: case PACKET_TLV_TYPE_PLAIN_RESPONSE: if (command->response.handler) res = command->response.handler(remote, packet); break; default: res = ERROR_NOT_FOUND; break; } return res; }
// returns the next byte to send - first the packet type, then the data length // then the channel and then the data (len bytes). // // Used by UART code to include type and length in packet // // returns -1 if empty static int16_t read_char_from_packet(packet_t *buf) { int16_t rv = -1; switch (txstate) { case TX_TYPE: txstate = TX_LEN; rv = 0xff & packet_get_type(buf); //if (rv == FS_TERM && packet_get_chan(buf) == FSFD_SETOPT) led_on(); break; case TX_LEN: txstate = TX_CHANNEL; rv = (0xff & packet_get_contentlen(buf)) + 3; // plus 2 to adhere to packet wire format break; case TX_CHANNEL: rv = 0xff & packet_get_chan(buf); if (packet_get_contentlen(buf) == 0) { txstate = TX_IDLE; } else { txstate = TX_DATA; } break; case TX_DATA: rv = 0xff & packet_read_char(buf); if (!packet_has_data(buf)) { txstate = TX_IDLE; } break; default: break; } return rv; }
int packet_get_length(Packet* packet) { _Packet* p = (_Packet*)packet; switch (packet_get_type(packet)) { case PACKET_TYPE_SEND_MESSAGE: { return packet_get_byte(packet, SEND_MESSAGE_LENGTH) + 2; break; } case PACKET_TYPE_SEND_NAME: { return packet_get_byte(packet, SEND_NAME_LENGTH) + 2; break; } case PACKET_TYPE_PACKETS: { int num_packets; return packet_packet_get_length(p, &num_packets); break; } default: { return PACKET_SIZES[(int)(p->type)]; break; } } }
/* * Validate command arguments */ DWORD command_validate_arguments(Command *command, Packet *packet) { PacketDispatcher *dispatcher = NULL; PacketTlvType type = packet_get_type(packet); DWORD res = ERROR_SUCCESS, packetIndex, commandIndex; Tlv current; // Select the dispatcher table if ((type == PACKET_TLV_TYPE_RESPONSE) || (type == PACKET_TLV_TYPE_PLAIN_RESPONSE)) dispatcher = &command->response; else dispatcher = &command->request; // Enumerate the arguments, validating the meta types of each for (commandIndex = 0, packetIndex = 0; ((packet_enum_tlv(packet, packetIndex, TLV_TYPE_ANY, ¤t) == ERROR_SUCCESS) && (res == ERROR_SUCCESS)); commandIndex++, packetIndex++) { TlvMetaType tlvMetaType; // Check to see if we've reached the end of the command arguments if ((dispatcher->numArgumentTypes) && (commandIndex == (dispatcher->numArgumentTypes & ARGUMENT_FLAG_MASK))) { // If the repeat flag is set, reset the index if (commandIndex & ARGUMENT_FLAG_REPEAT) commandIndex = 0; else break; } // Make sure the argument is at least one of the meta types tlvMetaType = packet_get_tlv_meta(packet, ¤t); // Validate argument meta types switch (tlvMetaType) { case TLV_META_TYPE_STRING: if (packet_is_tlv_null_terminated(packet, ¤t) != ERROR_SUCCESS) res = ERROR_INVALID_PARAMETER; break; default: break; } if ((res != ERROR_SUCCESS) && (commandIndex < dispatcher->numArgumentTypes)) break; } return res; }
void packet_add_packet(Packet** packet_packet_pointer, Packet* packet) { Packet* packet_packet = *packet_packet_pointer; assert(packet_get_type(packet_packet) == PACKET_TYPE_PACKETS); assert(packet_get_type(packet) != PACKET_TYPE_PACKETS); int num_packets; int length = packet_packet_get_length((_Packet*)packet_packet, &num_packets);// - 1; int length_to_add = packet_get_length(packet); int total_length = length + length_to_add;// + 1; *packet_packet_pointer = (Packet*)realloc((char*)packet_packet, total_length); char* character_packet = (char*)(*packet_packet_pointer); character_packet[total_length - 1] = PACKET_TYPE_UNDEFINED; memcpy(character_packet + length - 1, packet, length_to_add); }
extern int udptest_send(t_connection const * c) { t_packet * upacket; struct sockaddr_in caddr; unsigned int tries,successes; memset(&caddr,0,sizeof(caddr)); caddr.sin_family = PSOCK_AF_INET; caddr.sin_port = htons(conn_get_game_port(c)); caddr.sin_addr.s_addr = htonl(conn_get_game_addr(c)); for (tries=successes=0; successes!=2 && tries<5; tries++) { if (!(upacket = packet_create(packet_class_udp))) { eventlog(eventlog_level_error,"udptest_send","[%d] could not allocate memory for packet",conn_get_socket(c)); continue; } packet_set_size(upacket,sizeof(t_server_udptest)); packet_set_type(upacket,SERVER_UDPTEST); bn_int_tag_set(&upacket->u.server_udptest.bnettag,BNETTAG); if (hexstrm) { fprintf(hexstrm,"%d: send class=%s[0x%02hx] type=%s[0x%04hx] ", conn_get_game_socket(c), packet_get_class_str(upacket),(unsigned int)packet_get_class(upacket), packet_get_type_str(upacket,packet_dir_from_server),packet_get_type(upacket)); fprintf(hexstrm,"from=%s ", addr_num_to_addr_str(conn_get_game_addr(c),conn_get_game_port(c))); fprintf(hexstrm,"to=%s ", addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr),ntohs(caddr.sin_port))); fprintf(hexstrm,"length=%u\n", packet_get_size(upacket)); hexdump(hexstrm,packet_get_raw_data(upacket,0),packet_get_size(upacket)); } if (psock_sendto(conn_get_game_socket(c), packet_get_raw_data_const(upacket,0),packet_get_size(upacket), 0,(struct sockaddr *)&caddr,(psock_t_socklen)sizeof(caddr))!=(int)packet_get_size(upacket)) eventlog(eventlog_level_error,"udptest_send","[%d] failed to send UDPTEST to %s (attempt %u) (psock_sendto: %s)",conn_get_socket(c),addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr),conn_get_game_port(c)),tries+1,strerror(psock_errno())); else successes++; packet_del_ref(upacket); } if (successes!=2) return -1; return 0; }
static int packet_packet_get_length(_Packet* p, int* num_packets) { assert(packet_get_type((Packet*)p) == PACKET_TYPE_PACKETS); int total_length = 1; char* character_packet = (char*)p; int counter = 0; while (1) { Packet* next_packet = (Packet*)(character_packet + total_length); if (packet_get_type(next_packet) == PACKET_TYPE_UNDEFINED) { break; } counter++; total_length += packet_get_length(next_packet); } *num_packets = counter; return total_length + 1; }
/* * Create a response packet from a request, referencing the requestors * message identifier. */ Packet *packet_create_response(Packet *request) { Packet *response = NULL; Tlv method, requestId; BOOL success = FALSE; PacketTlvType responseType; if (packet_get_type(request) == PACKET_TLV_TYPE_PLAIN_REQUEST) responseType = PACKET_TLV_TYPE_PLAIN_RESPONSE; else responseType = PACKET_TLV_TYPE_RESPONSE; do { // Get the request TLV's method if (packet_get_tlv_string(request, TLV_TYPE_METHOD, &method) != ERROR_SUCCESS) break; // Try to allocate a response packet if (!(response = packet_create(responseType, (PCHAR)method.buffer))) break; // Get the request TLV's request identifier if (packet_get_tlv_string(request, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) break; // Add the request identifier to the packet packet_add_tlv_string(response, TLV_TYPE_REQUEST_ID, (PCHAR)requestId.buffer); success = TRUE; } while (0); // Cleanup on failure if (!success) { if (response) packet_destroy(response); response = NULL; } return response; }
static int sd_tcpoutput(int csocket, t_connection * c) { unsigned int currsize; unsigned int totsize; t_packet * packet; totsize = 0; for (;;) { currsize = conn_get_out_size(c); switch (net_send_packet(csocket,queue_peek_packet((t_queue const * const *)conn_get_out_queue(c)),&currsize)) /* avoid warning */ { case -1: conn_destroy(c); return -1; case 0: /* still working on it */ conn_set_out_size(c,currsize); return 0; /* bail out */ case 1: /* done sending */ packet = queue_pull_packet(conn_get_out_queue(c)); if (hexstrm) { fprintf(hexstrm,"%d: send 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_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); conn_set_out_size(c,0); /* stop at about 2KB (or until out of packets or EWOULDBLOCK) */ if (totsize>2048 || queue_get_length((t_queue const * const *)conn_get_out_queue(c))<1) return 0; totsize += currsize; } } /* not reached */ }
Packet* packet_get_packet(Packet* packet_packet, int index) { char* character_packet = (char*)packet_packet; int packet_index = 1; for (int i = 0; i < index; i++) { Packet* next_packet = (Packet*)(character_packet + packet_index); if (packet_get_type(next_packet) == PACKET_TYPE_UNDEFINED) { break; } packet_index += packet_get_length(next_packet); } return (Packet*)(character_packet + packet_index); }
// there shouldn't be much debug output, as sending it may invariably // receive the next option, triggering the option again. But it isn't // re-entrant! static uint8_t setopt_callback(int8_t channelno, int8_t errno, packet_t *rxpacket) { //debug_printf("setopt cb err=%d\n", errno); if (errno == CBM_ERROR_OK) { //debug_printf("rx command: %s\n", buf); uint8_t cmd = packet_get_type(rxpacket); uint8_t len = packet_get_contentlen(rxpacket); switch(cmd) { case FS_SETOPT: do_setopt(buf, len); break; case FS_RESET: rtconfig_pullconfig(0, NULL); break; } } // callback returns 1 to continue receiving on this channel return 1; }
// there shouldn't be much debug output, as sending it may invariably // receive the next option, triggering the option again. But it isn't // re-entrant! static uint8_t out_callback(int8_t channelno, int8_t errno, packet_t *rxpacket) { int8_t outrv; //debug_printf("setopt cb err=%d\n", errno); if (errno == CBM_ERROR_OK) { //debug_printf("rx command: %s\n", buf); uint8_t cmd = packet_get_type(rxpacket); switch(cmd) { case FS_REPLY: outrv = packet_get_buffer(rxpacket)[0]; if (outrv != CBM_ERROR_OK) { // fallback to ASCII current_charset = CHARSET_ASCII; } endpoint->provider->set_charset(endpoint->provdata, current_charset); break; } } device_unlock(); // callback returns 1 to continue receiving on this channel return 0; }
extern int main(int argc, char * argv[]) { int a; int sd; struct sockaddr_in saddr; t_packet * packet; t_packet * rpacket; t_packet * fpacket; char const * clienttag=NULL; char const * archtag=NULL; char const * servname=NULL; unsigned short servport=0; char const * hexfile=NULL; char text[MAX_MESSAGE_LEN]; char const * reqfile=NULL; struct hostent * host; unsigned int commpos; struct termios in_attr_old; struct termios in_attr_new; int changed_in; unsigned int currsize; unsigned int filelen; unsigned int startoffset; int startoffsetoverride=0; #define EXIST_ACTION_UNSPEC -1 #define EXIST_ACTION_ASK 0 #define EXIST_ACTION_OVERWRITE 1 #define EXIST_ACTION_BACKUP 2 #define EXIST_ACTION_RESUME 3 int exist_action=EXIST_ACTION_UNSPEC; struct stat exist_buf; char const * filename; FILE * fp; FILE * hexstrm=NULL; int fd_stdin; t_bnettime bntime; time_t tm; char timestr[FILE_TIME_MAXLEN]; unsigned int screen_width,screen_height; int munged; if (argc<1 || !argv || !argv[0]) { fprintf(stderr,"bad arguments\n"); return STATUS_FAILURE; } for (a=1; a<argc; a++) if (servname && isdigit((int)argv[a][0]) && a+1>=argc) { if (str_to_ushort(argv[a],&servport)<0) { fprintf(stderr,"%s: \"%s\" should be a positive integer\n",argv[0],argv[a]); usage(argv[0]); } } else if (!servname && argv[a][0]!='-' && a+2>=argc) servname = argv[a]; else if (strcmp(argv[a],"-b")==0 || strcmp(argv[a],"--client=SEXP")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_BROODWARS; } else if (strcmp(argv[a],"-d")==0 || strcmp(argv[a],"--client=DRTL")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLORTL; } else if (strcmp(argv[a],"--client=DSHR")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLOSHR; } else if (strcmp(argv[a],"-s")==0 || strcmp(argv[a],"--client=STAR")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_STARCRAFT; } else if (strcmp(argv[a],"--client=SSHR")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_SHAREWARE; } else if (strcmp(argv[a],"-w")==0 || strcmp(argv[a],"--client=W2BN")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WARCIIBNE; } else if (strcmp(argv[a],"--client=D2DV")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2DV; } else if (strcmp(argv[a],"--client=D2XP")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2XP; } else if (strcmp(argv[a],"--client=WAR3")==0) { if (clienttag) { fprintf(stderr,"%s: client type was already specified as \"%s\"\n",argv[0],clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WARCRAFT3; } else if (strncmp(argv[a],"--client=",9)==0) { fprintf(stderr,"%s: unknown client tag \"%s\"\n",argv[0],&argv[a][9]); usage(argv[0]); } else if (strncmp(argv[a],"--hexdump=",10)==0) { if (hexfile) { fprintf(stderr,"%s: hexdump file was already specified as \"%s\"\n",argv[0],hexfile); usage(argv[0]); } hexfile = &argv[a][10]; } else if (strcmp(argv[a],"--arch=IX86")==0) { if (archtag) { fprintf(stderr,"%s: client arch was already specified as \"%s\"\n",argv[0],archtag); usage(argv[0]); } archtag = ARCHTAG_WINX86; } else if (strcmp(argv[a],"--arch=PMAC")==0) { if (archtag) { fprintf(stderr,"%s: client arch was already specified as \"%s\"\n",argv[0],archtag); usage(argv[0]); } archtag = ARCHTAG_MACPPC; } else if (strcmp(argv[a],"--arch=XMAC")==0) { if (archtag) { fprintf(stderr,"%s: client arch was already specified as \"%s\"\n",argv[0],archtag); usage(argv[0]); } archtag = ARCHTAG_OSXPPC; } else if (strncmp(argv[a],"--arch=",7)==0) { fprintf(stderr,"%s: unknown arch tag \"%s\"\n",argv[0],&argv[a][9]); usage(argv[0]); } else if (strncmp(argv[a],"--startoffset=",14)==0) { if (startoffsetoverride) { fprintf(stderr,"%s: startoffset was already specified as %u\n",argv[0],startoffset); usage(argv[0]); } if (str_to_uint(&argv[a][14],&startoffset)<0) { fprintf(stderr,"%s: startoffset \"%s\" should be a positive integer\n",argv[0],&argv[a][14]); usage(argv[0]); } startoffsetoverride = 1; } else if (strncmp(argv[a],"--exists=",9)==0) { if (exist_action!=EXIST_ACTION_UNSPEC) { fprintf(stderr,"%s: exists was already specified\n",argv[0]); usage(argv[0]); } if (argv[a][9]=='o' || argv[a][9]=='O') exist_action = EXIST_ACTION_OVERWRITE; else if (argv[a][9]=='a' || argv[a][9]=='A') exist_action = EXIST_ACTION_ASK; else if (argv[a][9]=='b' || argv[a][9]=='B') exist_action = EXIST_ACTION_BACKUP; else if (argv[a][9]=='r' || argv[a][9]=='R') exist_action = EXIST_ACTION_RESUME; else { fprintf(stderr,"%s: exists must begin with a,A,o,O,b,B,r or R",argv[0]); usage(argv[0]); } } else if (strncmp(argv[a],"--file=",7)==0) { if (reqfile) { fprintf(stderr,"%s: file was already specified as \"%s\"\n",argv[0],reqfile); usage(argv[0]); } reqfile = &argv[a][7]; } else if (strcmp(argv[a],"-v")==0 || strcmp(argv[a],"--version")==0) { printf("version "BNETD_VERSION"\n"); return 0; } else if (strcmp(argv[a],"-h")==0 || strcmp(argv[a],"--help")==0 || strcmp(argv[a],"--usage")==0) usage(argv[0]); else if (strcmp(argv[a],"--client")==0 || strcmp(argv[a],"--hexdump")==0) { fprintf(stderr,"%s: option \"%s\" requires an argument\n",argv[0],argv[a]); usage(argv[0]); } else { fprintf(stderr,"%s: unknown option \"%s\"\n",argv[0],argv[a]); usage(argv[0]); } if (servport==0) servport = BNETD_SERV_PORT; if (!clienttag) clienttag = CLIENTTAG_STARCRAFT; if (!archtag) archtag = ARCHTAG_WINX86; if (!servname) servname = BNETD_DEFAULT_HOST; if (exist_action==EXIST_ACTION_UNSPEC) exist_action = EXIST_ACTION_ASK; if (hexfile) if (!(hexstrm = fopen(hexfile,"w"))) fprintf(stderr,"%s: could not open file \"%s\" for writing the hexdump (fopen: %s)",argv[0],hexfile,strerror(errno)); else fprintf(hexstrm,"# dump generated by bnftp version "BNETD_VERSION"\n"); if (psock_init()<0) { fprintf(stderr,"%s: could not inialialize socket functions\n",argv[0]); return STATUS_FAILURE; } if (!(host = gethostbyname(servname))) { fprintf(stderr,"%s: unknown host \"%s\"\n",argv[0],servname); return STATUS_FAILURE; } fd_stdin = fileno(stdin); if (tcgetattr(fd_stdin,&in_attr_old)>=0) { in_attr_new = in_attr_old; in_attr_new.c_lflag &= ~(ECHO | ICANON); /* turn off ECHO and ICANON */ in_attr_new.c_cc[VMIN] = 1; /* require reads to return at least one byte */ in_attr_new.c_cc[VTIME] = 0; /* no timeout */ tcsetattr(fd_stdin,TCSANOW,&in_attr_new); changed_in = 1; } else { fprintf(stderr,"%s: could not get terminal attributes for stdin\n",argv[0]); changed_in = 0; } if (client_get_termsize(fd_stdin,&screen_width,&screen_height)<0) { fprintf(stderr,"%s: could not determine screen size\n",argv[0]); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); return STATUS_FAILURE; } if ((sd = psock_socket(PSOCK_PF_INET,PSOCK_SOCK_STREAM,PSOCK_IPPROTO_TCP))<0) { fprintf(stderr,"%s: could not create socket (psock_socket: %s)\n",argv[0],strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); return STATUS_FAILURE; } memset(&saddr,0,sizeof(saddr)); saddr.sin_family = PSOCK_AF_INET; saddr.sin_port = htons(servport); memcpy(&saddr.sin_addr.s_addr,host->h_addr_list[0],host->h_length); if (psock_connect(sd,(struct sockaddr *)&saddr,sizeof(saddr))<0) { fprintf(stderr,"%s: could not connect to server \"%s\" port %hu (psock_connect: %s)\n",argv[0],servname,servport,strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); return STATUS_FAILURE; } printf("Connected to %s:%hu.\n",inet_ntoa(saddr.sin_addr),servport); #ifdef CLIENTDEBUG eventlog_set(stderr); #endif if (!(packet = packet_create(packet_class_init))) { fprintf(stderr,"%s: could not create packet\n",argv[0]); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); return STATUS_FAILURE; } bn_byte_set(&packet->u.client_initconn.class,CLIENT_INITCONN_CLASS_FILE); if (hexstrm) { fprintf(hexstrm,"%d: send class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, 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(packet,0),packet_get_size(packet)); } client_blocksend_packet(sd,packet); packet_del_ref(packet); if (!(rpacket = packet_create(packet_class_file))) { fprintf(stderr,"%s: could not create packet\n",argv[0]); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); return STATUS_FAILURE; } if (!(fpacket = packet_create(packet_class_raw))) { fprintf(stderr,"%s: could not create packet\n",argv[0]); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); packet_del_ref(rpacket); return STATUS_FAILURE; } if (!reqfile) /* if not specified on the command line then prompt for it */ { munged = 1; commpos = 0; text[0] = '\0'; for (;;) { switch (client_get_comm("filename: ",text,sizeof(text),&commpos,1,munged,screen_width)) { case -1: /* cancel or error */ printf("\n"); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; case 0: /* timeout */ munged = 0; continue; case 1: munged = 0; if (text[0]=='\0') continue; printf("\n"); } break; } reqfile = text; } if (stat(reqfile,&exist_buf)==0) /* check if the file exists */ { char text2[MAX_MESSAGE_LEN]; munged = 1; commpos = 0; text2[0] = '\0'; while (exist_action==EXIST_ACTION_ASK) { switch (client_get_comm("File exists [O]verwrite, [B]ackup or [R]esume?: ",text2,sizeof(text2),&commpos,1,munged,screen_width)) { case -1: /* cancel or error */ printf("\n"); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; case 0: /* timeout */ munged = 0; continue; case 1: munged = 0; if (text2[0]=='\0') continue; printf("\n"); break; } switch (text2[0]) { case 'o': case 'O': exist_action = EXIST_ACTION_OVERWRITE; break; case 'b': case 'B': exist_action = EXIST_ACTION_BACKUP; break; case 'r': case 'R': exist_action = EXIST_ACTION_RESUME; break; default: printf("Please answer with o,O,b,B,r or R.\n"); munged = 1; continue; } break; } switch (exist_action) { case EXIST_ACTION_OVERWRITE: if (!startoffsetoverride) startoffset = 0; break; case EXIST_ACTION_BACKUP: { char * bakfile; unsigned int bnr; int renamed=0; if (!(bakfile = malloc(strlen(reqfile)+1+2+1))) /* assuming we go up to bnr 99 we need reqfile+'.'+'99'+'\0' */ { fprintf(stderr,"%s: unable to allocate memory for backup filename.\n",argv[0]); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } for (bnr=0; bnr<100; bnr++) { sprintf(bakfile,"%s.%d",reqfile,bnr); if (stat(bakfile,&exist_buf)==0) continue; /* backup exists */ /* backup does not exist */ if (rename(reqfile,bakfile)<0) /* just rename the existing file to the backup */ fprintf(stderr,"%s: could not create backup file \"%s\" (rename: %s)\n",argv[0],bakfile,strerror(errno)); else { renamed = 1; printf("Renaming \"%s\" to \"%s\".\n",reqfile,bakfile); } break; } free(bakfile); if (!renamed) { fprintf(stderr,"%s: could not create backup for \"%s\".\n",argv[0],reqfile); if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } if (!startoffsetoverride) startoffset = 0; } break; case EXIST_ACTION_RESUME: if (!startoffsetoverride) startoffset = exist_buf.st_size; break; } } else if (!startoffsetoverride) startoffset = 0; if (changed_in) tcsetattr(fd_stdin,TCSAFLUSH,&in_attr_old); if (!(packet = packet_create(packet_class_file))) { fprintf(stderr,"%s: could not create packet\n",argv[0]); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } packet_set_size(packet,sizeof(t_client_file_req)); packet_set_type(packet,CLIENT_FILE_REQ); bn_int_tag_set(&packet->u.client_file_req.archtag,archtag); bn_int_tag_set(&packet->u.client_file_req.clienttag,clienttag); bn_int_set(&packet->u.client_file_req.adid,0); bn_int_set(&packet->u.client_file_req.extensiontag,0); bn_int_set(&packet->u.client_file_req.startoffset,startoffset); bn_long_set_a_b(&packet->u.client_file_req.timestamp,0x00000000,0x00000000); packet_append_string(packet,reqfile); if (hexstrm) { fprintf(hexstrm,"%d: send class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, 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(packet,0),packet_get_size(packet)); } printf("\nRequesting info..."); fflush(stdout); client_blocksend_packet(sd,packet); packet_del_ref(packet); do { if (client_blockrecv_packet(sd,rpacket)<0) { fprintf(stderr,"%s: server closed connection\n",argv[0]); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } if (hexstrm) { fprintf(hexstrm,"%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(rpacket),(unsigned int)packet_get_class(rpacket), packet_get_type_str(rpacket,packet_dir_from_server),packet_get_type(rpacket), packet_get_size(rpacket)); hexdump(hexstrm,packet_get_raw_data(rpacket,0),packet_get_size(rpacket)); } } while (packet_get_type(rpacket)!=SERVER_FILE_REPLY); filelen = bn_int_get(rpacket->u.server_file_reply.filelen); bn_long_to_bnettime(rpacket->u.server_file_reply.timestamp,&bntime); tm = bnettime_to_time(bntime); strftime(timestr,FILE_TIME_MAXLEN,FILE_TIME_FORMAT,localtime(&tm)); filename = packet_get_str_const(rpacket,sizeof(t_server_file_reply),MAX_FILENAME_STR); if (exist_action==EXIST_ACTION_RESUME) { if (!(fp = fopen(reqfile,"ab"))) { fprintf(stderr,"%s: could not open file \"%s\" for appending (fopen: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } } else { if (!(fp = fopen(reqfile,"wb"))) { fprintf(stderr,"%s: could not open file \"%s\" for writing (fopen: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } } printf("\n name: \""); str_print_term(stdout,filename,0,0); printf("\"\n changed: %s\n length: %u bytes\n",timestr,filelen); fflush(stdout); if (startoffset>0) { filelen -= startoffset; /* for resuming files */ printf("Resuming at position %u (%u bytes remaining).\n",startoffset,filelen); } printf("\nSaving to \"%s\"...",reqfile); for (currsize=0; currsize+MAX_PACKET_SIZE<=filelen; currsize+=MAX_PACKET_SIZE) { printf("."); fflush(stdout); if (client_blockrecv_raw_packet(sd,fpacket,MAX_PACKET_SIZE)<0) { printf("error\n"); fprintf(stderr,"%s: server closed connection\n",argv[0]); if (fclose(fp)<0) fprintf(stderr,"%s: could not close file \"%s\" after writing (fclose: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } if (hexstrm) { fprintf(hexstrm,"%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket),(unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket,packet_dir_from_server),packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm,packet_get_raw_data(fpacket,0),packet_get_size(fpacket)); } if (fwrite(packet_get_raw_data_const(fpacket,0),1,MAX_PACKET_SIZE,fp)<MAX_PACKET_SIZE) { printf("error\n"); fprintf(stderr,"%s: could not write to file \"%s\" (fwrite: %s)\n",argv[0],reqfile,strerror(errno)); if (fclose(fp)<0) fprintf(stderr,"%s: could not close file \"%s\" after writing (fclose: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } } filelen -= currsize; if (filelen) { printf("."); fflush(stdout); if (client_blockrecv_raw_packet(sd,fpacket,filelen)<0) { printf("error\n"); fprintf(stderr,"%s: server closed connection\n",argv[0]); if (fclose(fp)<0) fprintf(stderr,"%s: could not close file \"%s\" after writing (fclose: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } if (hexstrm) { fprintf(hexstrm,"%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket),(unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket,packet_dir_from_server),packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm,packet_get_raw_data(fpacket,0),packet_get_size(fpacket)); } if (fwrite(packet_get_raw_data_const(fpacket,0),1,filelen,fp)<filelen) { printf("error\n"); fprintf(stderr,"%s: could not write to file \"%s\"\n",argv[0],reqfile); if (fclose(fp)<0) fprintf(stderr,"%s: could not close file \"%s\" after writing (fclose: %s)\n",argv[0],reqfile,strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return STATUS_FAILURE; } } packet_del_ref(fpacket); packet_del_ref(rpacket); if (hexstrm) { fprintf(hexstrm,"# end of dump\n"); if (fclose(hexstrm)<0) fprintf(stderr,"%s: could not close hexdump file \"%s\" after writing (fclose: %s)",argv[0],hexfile,strerror(errno)); } if (fclose(fp)<0) { fprintf(stderr,"%s: could not close file \"%s\" after writing (fclose: %s)\n",argv[0],reqfile,strerror(errno)); return STATUS_FAILURE; } printf("done\n"); return STATUS_FAILURE; }
extern int handle_file_packet(t_connection * c, t_packet const * const packet) { if (!c) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got NULL connection",conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got NULL packet",conn_get_socket(c)); return -1; } /* REMOVED BY UNDYING SOULZZ 4/3/02 */ /* if (packet_get_class(packet)!=packet_class_file) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got bad packet (class %d)",conn_get_socket(c),(int)packet_get_class(packet)); return -1; } */ switch (conn_get_state(c)) { case conn_state_connected: switch (packet_get_type(packet)) { case CLIENT_FILE_REQ: { char const * rawname; if (!(rawname = packet_get_str_const(packet,sizeof(t_client_file_req),MAX_FILENAME_STR))) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got bad FILE_REQ (missing or too long filename)",conn_get_socket(c)); return -1; } file_send(c,rawname, bn_int_get(packet->u.client_file_req.adid), bn_int_get(packet->u.client_file_req.extensiontag), bn_int_get(packet->u.client_file_req.startoffset), 1); } break; case CLIENT_FILE_REQ2: { t_packet * rpacket = NULL; if((rpacket = packet_create(packet_class_raw))) { packet_set_size(rpacket,sizeof(t_server_file_unknown1)); bn_int_set( &rpacket->u.server_file_unknown1.unknown, 0xdeadbeef ); conn_push_outqueue(c, rpacket ); packet_del_ref( rpacket ); } conn_set_state(c, conn_state_pending_raw); break; } default: eventlog(eventlog_level_error,__FUNCTION__,"[%d] unknown file packet type 0x%04x, len %u",conn_get_socket(c),packet_get_type(packet),packet_get_size(packet)); break; } break; case conn_state_pending_raw: switch (packet_get_type(packet)) { case CLIENT_FILE_REQ3: { char rawname[MAX_FILENAME_STR]; psock_recv( conn_get_socket(c), rawname, MAX_FILENAME_STR, 0 ); file_send(c, rawname, 0, 0, 0, 1); } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[%d] unknown file packet type 0x%04x, len %u",conn_get_socket(c),packet_get_type(packet),packet_get_size(packet)); break; } break; default: eventlog(eventlog_level_error,__FUNCTION__,"[%d] unknown file connection state %d",conn_get_socket(c),(int)conn_get_state(c)); } return 0; }
void packet_print(Packet* packet) { switch (packet_get_type(packet)) { case PACKET_TYPE_SEND_MESSAGE: { printf("Send Message: Length=%d, Message=%s\n", packet_get_length(packet), packet_get_string(packet, SEND_MESSAGE_TEXT)); break; } case PACKET_TYPE_SEND_NAME: { printf("Send Name: Length=%d, Name=%s\n", packet_get_length(packet), packet_get_string(packet, SEND_NAME_NAME)); break; } case PACKET_TYPE_MOUSE_DOWN: { printf("Mouse Down: Length=%d, X=%lf, Y=%lf\n", packet_get_length(packet), packet_get_double(packet, MOUSE_DOWN_X), packet_get_double(packet, MOUSE_DOWN_Y)); break; } case PACKET_TYPE_MOUSE_MOVE: { printf("Mouse Move: Length=%d, X=%lf, Y=%lf\n", packet_get_length(packet), packet_get_double(packet, MOUSE_MOVE_X), packet_get_double(packet, MOUSE_MOVE_Y)); break; } case PACKET_TYPE_MOUSE_UP: { printf("Mouse Up: Length=%d, X=%lf, Y=%lf\n", packet_get_length(packet), packet_get_double(packet, MOUSE_UP_X), packet_get_double(packet, MOUSE_UP_Y)); break; } case PACKET_TYPE_NEW_STROKE: { printf("New Stroke: Length=%d, ID=%d, X=%lf, Y=%lf, TYPE=%d, FILL=%d, WIDTH=%lf, R=%lf, G=%lf, B=%lf\n", packet_get_length(packet), packet_get_int(packet, NEW_STROKE_ID), packet_get_double(packet, NEW_STROKE_X), packet_get_double(packet, NEW_STROKE_Y), packet_get_byte(packet, NEW_STROKE_SHAPE_TYPE), packet_get_byte(packet, NEW_STROKE_FILL), packet_get_double(packet, NEW_STROKE_WIDTH), packet_get_double(packet, NEW_STROKE_R), packet_get_double(packet, NEW_STROKE_G), packet_get_double(packet, NEW_STROKE_B)); break; } case PACKET_TYPE_ADD_POINT: { printf("Add Point: Length=%d, ID=%d, X=%lf, Y=%lf\n", packet_get_length(packet), packet_get_int(packet, ADD_POINT_ID), packet_get_double(packet, ADD_POINT_X), packet_get_double(packet, ADD_POINT_Y)); break; } case PACKET_TYPE_DELETE: { printf("New Stroke: Length=%d, ID=%d\n", packet_get_length(packet), packet_get_int(packet, DELETE_ID)); break; } case PACKET_TYPE_UPDATE: { printf("Update: Length=%d, ID=%d, X=%lf, Y=%lf, T=%lf\n", packet_get_length(packet), packet_get_int(packet, UPDATE_ID), packet_get_double(packet, UPDATE_X), packet_get_double(packet, UPDATE_Y), packet_get_double(packet, UPDATE_ROTATION)); break; } case PACKET_TYPE_PACKETS: { printf("Packets: Length=%d\n", packet_get_length(packet)); int i = 0; Packet* p = NULL; do { printf("\t"); p = packet_get_packet(packet, i); packet_print(p); i++; } while (packet_get_type(p) != PACKET_TYPE_UNDEFINED); break; } case PACKET_TYPE_NEW_CONNECTION: { printf("New Connection: Length=1\n"); break; } case PACKET_TYPE_INK_POT: { printf("Ink: %d\n", packet_get_byte(packet, INK_POT_INK)); break; } case PACKET_TYPE_UNDEFINED: default: { printf("Packet Undefined\n"); break; } } }
/*! * @brief Receive a new packet on the given remote endpoint. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to a pointer that will receive the \c Packet data. * @return An indication of the result of processing the transmission request. */ static DWORD packet_receive_via_ssl(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; PacketHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; TcpTransportContext* ctx = (TcpTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); do { // Read the packet length while (inHeader) { if ((bytesRead = SSL_read(ctx->ssl, ((PUCHAR)&header + headerBytes), sizeof(PacketHeader)-headerBytes)) <= 0) { if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); } if (bytesRead < 0) { dprintf("[PACKET] receive header failed with error code %d. SSLerror=%d, WSALastError=%d\n", bytesRead, SSL_get_error(ctx->ssl, bytesRead), WSAGetLastError()); SetLastError(ERROR_NOT_FOUND); } break; } headerBytes += bytesRead; if (headerBytes != sizeof(PacketHeader)) { continue; } inHeader = FALSE; } if (headerBytes != sizeof(PacketHeader)) { break; } header.xor_key = ntohl(header.xor_key); // xor the header data xor_bytes(header.xor_key, &header.length, 8); // Initialize the header header.length = ntohl(header.length); // use TlvHeader size here, because the length doesn't include the xor byte payloadLength = header.length - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload while (payloadBytesLeft > 0) { if ((bytesRead = SSL_read(ctx->ssl, payload + payloadLength - payloadBytesLeft, payloadBytesLeft)) <= 0) { if (GetLastError() == WSAEWOULDBLOCK) { continue; } if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); } if (bytesRead < 0) { dprintf("[PACKET] receive payload of length %d failed with error code %d. SSLerror=%d\n", payloadLength, bytesRead, SSL_get_error(ctx->ssl, bytesRead)); SetLastError(ERROR_NOT_FOUND); } break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) { break; } xor_bytes(header.xor_key, payload, payloadLength); // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } memset(localPacket, 0, sizeof(Packet)); // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength, &payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) { free(payload); } if (localPacket) { free(localPacket); } } lock_release(remote->lock); return res; }
extern int handle_init_packet(t_connection * c, t_packet const * const packet) { if (!c) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got NULL connection",conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got NULL packet",conn_get_socket(c)); return -1; } if (packet_get_class(packet)!=packet_class_init) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got bad packet (class %d)",conn_get_socket(c),(int)packet_get_class(packet)); return -1; } switch (packet_get_type(packet)) { case CLIENT_INITCONN: switch (bn_byte_get(packet->u.client_initconn.class)) { case CLIENT_INITCONN_CLASS_BNET: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated bnet connection",conn_get_socket(c)); conn_set_state(c,conn_state_connected); conn_set_class(c,conn_class_bnet); break; case CLIENT_INITCONN_CLASS_FILE: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated file download connection",conn_get_socket(c)); conn_set_state(c,conn_state_connected); conn_set_class(c,conn_class_file); break; case CLIENT_INITCONN_CLASS_BOT: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated chat bot connection",conn_get_socket(c)); conn_set_state(c,conn_state_connected); conn_set_class(c,conn_class_bot); break; case CLIENT_INITCONN_CLASS_TELNET: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated telnet connection",conn_get_socket(c)); conn_set_state(c,conn_state_connected); conn_set_class(c,conn_class_telnet); break; case CLIENT_INITCONN_CLASS_D2CS_BNETD: { eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated d2cs_bnetd connection",conn_get_socket(c)); if (!(realmlist_find_realm_by_ip(conn_get_addr(c)))) { eventlog(eventlog_level_info,__FUNCTION__, "[%d] d2cs connection from unknown ip address %s",conn_get_socket(c),addr_num_to_addr_str(conn_get_addr(c),conn_get_port(c))); return -1; } conn_set_state(c,conn_state_connected); conn_set_class(c,conn_class_d2cs_bnetd); if (handle_d2cs_init(c)<0) { eventlog(eventlog_level_info,__FUNCTION__,"faild to init d2cs connection"); return -1; } } break; case CLIENT_INITCONN_CLASS_ENC: eventlog(eventlog_level_info,__FUNCTION__,"[%d] client initiated encrypted connection (not supported)",conn_get_socket(c)); return -1; default: eventlog(eventlog_level_error,__FUNCTION__,"[%d] client requested unknown class 0x%02x (length %d) (closing connection)",conn_get_socket(c),(unsigned int)bn_byte_get(packet->u.client_initconn.class),packet_get_size(packet)); return -1; } break; default: eventlog(eventlog_level_error,__FUNCTION__,"[%d] unknown init packet type 0x%04x, len %u",conn_get_socket(c),packet_get_type(packet),packet_get_size(packet)); return -1; } return 0; }
/*! * @brief Process a single command in a seperate thread of execution. * @param thread Pointer to the thread to execute. * @return Result of processing. */ DWORD THREADCALL command_process_thread( THREAD * thread ) { DWORD index = 0; DWORD result = ERROR_SUCCESS; Tlv methodTlv = {0}; Tlv requestIdTlv = {0}; PCHAR method = NULL; PCHAR requestId = NULL; Command * current = NULL; Remote * remote = NULL; Packet * packet = NULL; if( thread == NULL ) return ERROR_INVALID_HANDLE; remote = (Remote *)thread->parameter1; if( remote == NULL ) return ERROR_INVALID_HANDLE; packet = (Packet *)thread->parameter2; if( packet == NULL ) return ERROR_INVALID_DATA; if( commandThreadList == NULL ) { commandThreadList = list_create(); if( commandThreadList == NULL ) return ERROR_INVALID_HANDLE; #ifndef _WIN32 pthread_t tid; pthread_create(&tid, NULL, reap_zombie_thread, NULL); dprintf("reap_zombie_thread created, thread_id : 0x%x",tid); #endif } list_add( commandThreadList, thread ); __try { do { // Extract the method result = packet_get_tlv_string( packet, TLV_TYPE_METHOD, &methodTlv ); if( result != ERROR_SUCCESS ) break; dprintf( "[COMMAND] Processing method %s", methodTlv.buffer ); #ifdef _WIN32 // Impersonate the thread token if needed (only on Windows) if(remote->hServerToken != remote->hThreadToken) { if(! ImpersonateLoggedOnUser(remote->hThreadToken)) { dprintf( "[COMMAND] Failed to impersonate thread token (%s) (%u)", methodTlv.buffer, GetLastError()); } } #endif // Get the request identifier if the packet has one. result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv ); if( result == ERROR_SUCCESS ) requestId = (PCHAR)requestIdTlv.buffer; method = (PCHAR)methodTlv.buffer; result = ERROR_NOT_FOUND; // Try to find a match in the dispatch type for( index = 0, result = ERROR_NOT_FOUND ; result == ERROR_NOT_FOUND && commands[index].method ; index++ ) { if( strcmp( commands[index].method, method ) ) continue; // Call the base handler result = command_call_dispatch( &commands[index], remote, packet ); } // Regardless of error code, try to see if someone has overriden a base handler for( current = extension_commands, result = ERROR_NOT_FOUND ; result == ERROR_NOT_FOUND && current && current->method ; current = current->next ) { if( strcmp( current->method, method ) ) continue; // Call the custom handler result = command_call_dispatch( current, remote, packet ); } dprintf("[COMMAND] Calling completion handlers..."); // Finally, call completion routines for the provided identifier if( ((packet_get_type(packet) == PACKET_TLV_TYPE_RESPONSE) || (packet_get_type(packet) == PACKET_TLV_TYPE_PLAIN_RESPONSE)) && (requestId)) packet_call_completion_handlers( remote, packet, requestId ); // If we get here, we're successful. result = ERROR_SUCCESS; } while( 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { dprintf("[COMMAND] Exception hit in command thread 0x%08X!", thread ); } packet_destroy( packet ); if( list_remove( commandThreadList, thread ) ) thread_destroy( thread ); return ERROR_SUCCESS; }
/* * Receive a new packet over HTTP using WinInet */ DWORD packet_receive_http_via_wininet(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; TlvHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; DWORD flags; DWORD flen; HINTERNET hReq; HINTERNET hRes; DWORD retries = 5; lock_acquire( remote->lock ); do { flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_NO_UI; if (remote->transport == METERPRETER_TRANSPORT_HTTPS) { flags |= INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID; } dprintf("[PACKET RECEIVE] HttpOpenRequest"); hReq = HttpOpenRequest(remote->hConnection, "POST", remote->uri, NULL, NULL, NULL, flags, 0); if (hReq == NULL) { dprintf("[PACKET RECEIVE] Failed HttpOpenRequest: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } if (remote->transport == METERPRETER_TRANSPORT_HTTPS) { InternetQueryOption( hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, &flen); flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; InternetSetOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, flen); } hRes = HttpSendRequest(hReq, NULL, 0, "RECV", 4 ); if (! hRes) { dprintf("[PACKET RECEIVE] Failed HttpSendRequest: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } // Read the packet length retries = 3; while (inHeader && retries > 0) { retries--; if (! InternetReadFile(hReq, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes, &bytesRead)) { dprintf("[PACKET RECEIVE] Failed HEADER InternetReadFile: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } // If the response contains no data, this is fine, it just means the // remote side had nothing to tell us. Indicate this through a // ERROR_EMPTY response code so we can update the timestamp. if (bytesRead == 0) { SetLastError(ERROR_EMPTY); break; } headerBytes += bytesRead; if (headerBytes != sizeof(TlvHeader)) { continue; } else { inHeader = FALSE; } } if (GetLastError() == ERROR_EMPTY) break; if (headerBytes != sizeof(TlvHeader)) { SetLastError(ERROR_NOT_FOUND); break; } // Initialize the header header.length = header.length; header.type = header.type; payloadLength = ntohl(header.length) - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload retries = payloadBytesLeft; while (payloadBytesLeft > 0 && retries > 0 ) { retries--; if (! InternetReadFile(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead)) { dprintf("[PACKET RECEIVE] Failed BODY InternetReadFile: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); break; } if (!bytesRead) { SetLastError(ERROR_NOT_FOUND); break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) break; // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } memset( localPacket, 0, sizeof(Packet) ); // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength,&payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) free(payload); if (localPacket) free(localPacket); } if (hReq) InternetCloseHandle(hReq); lock_release( remote->lock ); return res; }
/* * Transmit and destroy a packet over HTTP(S) */ DWORD packet_transmit_via_http(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; #ifdef _UNIX int local_error = -1; #endif lock_acquire( remote->lock ); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } #ifdef _WIN32 dprintf("Transmitting packet of length %d to remote", packet->payloadLength); res = packet_transmit_via_http_wininet(remote, packet, completion); #else // XXX: Implement non-windows HTTP delivery #endif if(res < 0) { dprintf("[PACKET] transmit failed with return %d\n", res); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release( remote->lock ); return res; }
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); } } } }
/* * Transmit and destroy a packet */ DWORD packet_transmit(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; DWORD idx; #ifdef _UNIX int local_error = -1; #endif // If the packet does not already have a request identifier, create // one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } idx = 0; while( idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( remote->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if(res <= 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if(res < 0) break; idx = 0; while( idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( remote->ssl, packet->payload + idx, packet->payloadLength - idx ); if(res < 0) break; idx += res; } if(res < 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); return res; }
/* * Receive a new packet */ DWORD packet_receive(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; CryptoContext *crypto = NULL; Packet *localPacket = NULL; TlvHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR payload = NULL; ULONG payloadLength; #ifdef _UNIX int local_error = -1; #endif do { // Read the packet length while (inHeader) { if ((bytesRead = SSL_read(remote->ssl, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes)) <= 0) { if (!bytesRead) SetLastError(ERROR_NOT_FOUND); if(bytesRead < 0) { dprintf("receive header failed with error code %d\n", bytesRead); SetLastError(ERROR_NOT_FOUND); } break; } headerBytes += bytesRead; if (headerBytes != sizeof(TlvHeader)) continue; else inHeader = FALSE; } if (bytesRead != sizeof(TlvHeader)) break; // Initialize the header header.length = header.length; header.type = header.type; payloadLength = ntohl(header.length) - sizeof(TlvHeader); payloadBytesLeft = payloadLength; // Allocate the payload if (!(payload = (PUCHAR)malloc(payloadLength))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // Read the payload while (payloadBytesLeft > 0) { if ((bytesRead = SSL_read(remote->ssl, payload + payloadLength - payloadBytesLeft, payloadBytesLeft)) <= 0) { if (GetLastError() == WSAEWOULDBLOCK) continue; if (!bytesRead) SetLastError(ERROR_NOT_FOUND); if(bytesRead < 0) { dprintf("receive payload of length %d failed with error code %d\n", payloadLength, bytesRead); SetLastError(ERROR_NOT_FOUND); } break; } payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) break; // Allocate a packet structure if (!(localPacket = (Packet *)malloc(sizeof(Packet)))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } // If the connection has an established cipher and this packet is not // plaintext, decrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = payloadLength; PUCHAR origPayload = payload; // Decrypt if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength, &payload, &payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // We no longer need the encrypted payload free(origPayload); } localPacket->header.length = header.length; localPacket->header.type = header.type; localPacket->payload = payload; localPacket->payloadLength = payloadLength; *packet = localPacket; SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Cleanup on failure if (res != ERROR_SUCCESS) { if (payload) free(payload); if (localPacket) free(localPacket); } return res; }
/*! * @brief Create a response packet from a request. * @details Create a response packet from a request, referencing the requestors * message identifier. * @param request The request \c Packet to build a response for. * @return Pointer to a new \c Packet. */ Packet *packet_create_response(Packet *request) { Packet *response = NULL; Tlv method, requestId; BOOL success = FALSE; PacketTlvType responseType; if (packet_get_type(request) == PACKET_TLV_TYPE_PLAIN_REQUEST) { responseType = PACKET_TLV_TYPE_PLAIN_RESPONSE; } else { responseType = PACKET_TLV_TYPE_RESPONSE; } do { // Get the request TLV's method if (packet_get_tlv_string(request, TLV_TYPE_METHOD, &method) != ERROR_SUCCESS) { break; } // Try to allocate a response packet if (!(response = packet_create(responseType, (PCHAR)method.buffer))) { break; } // Get the request TLV's request identifier if (packet_get_tlv_string(request, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { break; } // Add the request identifier to the packet packet_add_tlv_string(response, TLV_TYPE_REQUEST_ID, (PCHAR)requestId.buffer); // If the packet that is being handled is considered local, then we // associate the response with the request so that it can be handled // locally (and vice versa) if (request->local) { request->partner = response; response->partner = request; } success = TRUE; } while (0); // Cleanup on failure if (!success) { if (response) { packet_destroy(response); } response = NULL; } return response; }
static int on_bnetd_charloginreply(t_connection * c, t_packet * packet) { unsigned int seqno; t_sq * sq; t_connection * client; t_packet * opacket, * rpacket; int result, reply, type; char const * charname; t_elem * elem; if (!packet || !c) return -1; seqno=bn_int_get(packet->u.d2cs_bnetd.h.seqno); if (!(sq=sqlist_find_sq(seqno))) { eventlog(eventlog_level_error,__FUNCTION__,"seqno %d not found",seqno); return -1; } if (!(client=d2cs_connlist_find_connection_by_sessionnum(sq_get_clientid(sq)))) { eventlog(eventlog_level_error,__FUNCTION__,"client %d not found",sq_get_clientid(sq)); sq_destroy(sq,&elem); return -1; } if (!(opacket=sq_get_packet(sq))) { eventlog(eventlog_level_error,__FUNCTION__,"previous packet missing (seqno: %d)",seqno); sq_destroy(sq,&elem); return -1; } type=packet_get_type(opacket); result=bn_int_get(packet->u.bnetd_d2cs_charloginreply.reply); if (type==CLIENT_D2CS_CREATECHARREQ) { charname=packet_get_str_const(opacket,sizeof(t_client_d2cs_createcharreq),MAX_CHARNAME_LEN); if (result==BNETD_D2CS_CHARLOGINREPLY_SUCCEED) { if (conn_check_multilogin(client,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"character %s is already logged in",charname); reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; } else { reply= D2CS_CLIENT_CREATECHARREPLY_SUCCEED; eventlog(eventlog_level_info,__FUNCTION__,"character %s authed",charname); d2cs_conn_set_charname(client,charname); d2cs_conn_set_state(client,conn_state_char_authed); } } else { reply = D2CS_CLIENT_CREATECHARREPLY_FAILED; eventlog(eventlog_level_error,__FUNCTION__,"failed to auth character %s",charname); } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_createcharreply)); packet_set_type(rpacket,D2CS_CLIENT_CREATECHARREPLY); bn_int_set(&rpacket->u.d2cs_client_createcharreply.reply,reply); conn_push_outqueue(client,rpacket); packet_del_ref(rpacket); } } else if (type==CLIENT_D2CS_CHARLOGINREQ) { charname=packet_get_str_const(opacket,sizeof(t_client_d2cs_charloginreq),MAX_CHARNAME_LEN); if (result==BNETD_D2CS_CHARLOGINREPLY_SUCCEED) { if (conn_check_multilogin(client,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"character %s is already logged in",charname); reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; } else { reply = D2CS_CLIENT_CHARLOGINREPLY_SUCCEED; eventlog(eventlog_level_info,__FUNCTION__,"character %s authed",charname); d2cs_conn_set_charname(client,charname); d2cs_conn_set_state(client,conn_state_char_authed); } } else { reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; eventlog(eventlog_level_error,__FUNCTION__,"failed to auth character %s",charname); } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_charloginreply)); packet_set_type(rpacket,D2CS_CLIENT_CHARLOGINREPLY); bn_int_set(&rpacket->u.d2cs_client_charloginreply.reply,reply); conn_push_outqueue(client,rpacket); packet_del_ref(rpacket); } } else { eventlog(eventlog_level_error,__FUNCTION__,"got bad packet type %d",type); sq_destroy(sq,&elem); return -1; } sq_destroy(sq,&elem); return 0; }
static int sd_udpinput(t_addr *const curr_laddr, const t_laddr_info *laddr_info, int ssocket, int usocket) { int err; psock_t_socklen errlen; t_packet * upacket; if (laddr_info == NULL) { (void)ssocket; } err = 0; errlen = sizeof(err); if (psock_getsockopt(usocket,PSOCK_SOL_SOCKET,PSOCK_SO_ERROR,&err,&errlen)<0) { eventlog(eventlog_level_error,"sd_udpinput","[%d] unable to read socket error (psock_getsockopt: %s)",usocket,strerror(psock_errno())); return -1; } if (errlen && err) /* if it was an error, there is no packet to read */ { eventlog(eventlog_level_error,"sd_udpinput","[%d] async UDP socket error notification (psock_getsockopt: %s)",usocket,strerror(err)); return -1; } if (!(upacket = packet_create(packet_class_udp))) { eventlog(eventlog_level_error,"sd_udpinput","could not allocate raw packet for input"); return -1; } { struct sockaddr_in fromaddr; psock_t_socklen fromlen; int len; fromlen = sizeof(fromaddr); if ((len = psock_recvfrom(usocket,packet_get_raw_data_build(upacket,0),MAX_PACKET_SIZE,0,(struct sockaddr *)&fromaddr,&fromlen))<0) { if ( #ifdef PSOCK_EINTR psock_errno()!=PSOCK_EINTR && #endif #ifdef PSOCK_EAGAIN psock_errno()!=PSOCK_EAGAIN && #endif #ifdef PSOCK_EWOULDBLOCK psock_errno()!=PSOCK_EWOULDBLOCK && #endif 1) eventlog(eventlog_level_error,"sd_udpinput","could not recv UDP datagram (psock_recvfrom: %s)",strerror(psock_errno())); packet_del_ref(upacket); return -1; } if (fromaddr.sin_family!=PSOCK_AF_INET) { eventlog(eventlog_level_error,"sd_udpinput","got UDP datagram with bad address family %d",(int)fromaddr.sin_family); packet_del_ref(upacket); return -1; } packet_set_size(upacket,len); if (hexstrm) { char tempa[32]; if (!addr_get_addr_str(curr_laddr,tempa,sizeof(tempa))) strcpy(tempa,"x.x.x.x:x"); fprintf(hexstrm,"%d: recv class=%s[0x%02x] type=%s[0x%04x] from=%s to=%s length=%u\n", usocket, packet_get_class_str(upacket),(unsigned int)packet_get_class(upacket), packet_get_type_str(upacket,packet_dir_from_client),packet_get_type(upacket), addr_num_to_addr_str(ntohl(fromaddr.sin_addr.s_addr),ntohs(fromaddr.sin_port)), tempa, packet_get_size(upacket)); hexdump(hexstrm,packet_get_raw_data(upacket,0),packet_get_size(upacket)); } handle_udp_packet(usocket,ntohl(fromaddr.sin_addr.s_addr),ntohs(fromaddr.sin_port),upacket); packet_del_ref(upacket); } return 0; }
/*! * @brief Transmit a packet via SSL _and_ destroy it. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the \c Packet that is to be sent. * @param completion Pointer to the completion routines to process. * @return An indication of the result of processing the transmission request. * @remark This uses an SSL-encrypted TCP channel, and does not imply the use of HTTPS. */ DWORD packet_transmit_via_ssl(Remote* remote, Packet* packet, PacketRequestCompletion* completion) { CryptoContext* crypto; Tlv requestId; DWORD res; DWORD idx; TcpTransportContext* ctx = (TcpTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid)-1] = 0; for (index = 0; index < sizeof(rid)-1; index++) { rid[index] = (rand() % 0x5e) + 0x21; } packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) { packet_add_completion_handler((LPCSTR)requestId.buffer, completion); } // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } dprintf("[PACKET] New xor key for sending"); packet->header.xor_key = rand_xor_key(); // before transmission, xor the whole lot, starting with the body xor_bytes(packet->header.xor_key, (LPBYTE)packet->payload, packet->payloadLength); // then the header xor_bytes(packet->header.xor_key, (LPBYTE)&packet->header.length, 8); // be sure to switch the xor header before writing packet->header.xor_key = htonl(packet->header.xor_key); idx = 0; while (idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( ctx->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if (res <= 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if (res < 0) { break; } idx = 0; while (idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( ctx->ssl, packet->payload + idx, packet->payloadLength - idx ); if (res < 0) { break; } idx += res; } if (res < 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release(remote->lock); return res; }
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; }