/* ipxparse.c */ /* Copyright 1996 Volker Lendecke, Goettingen, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipxutil.h" #define DUMPALLSAPS /* #define if you want to dump all SAP's */ struct ipx_address { unsigned long net; unsigned char node[IPX_NODE_LEN]; unsigned short sock; }; struct ipx_packet { unsigned short ipx_checksum; #define IPX_NO_CHECKSUM 0xFFFF unsigned short ipx_pktsize; unsigned char ipx_tctrl; unsigned char ipx_type; #define IPX_TYPE_UNKNOWN 0x00 #define IPX_TYPE_RIP 0x01 /* may also be 0 */ #define IPX_TYPE_SAP 0x04 /* may also be 0 */ #define IPX_TYPE_SPX 0x05 /* Not yet implemented */ #define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */ #define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */ struct ipx_address ipx_dest __attribute__ ((packed)); struct ipx_address ipx_source __attribute__ ((packed)); }; #define NCP_ALLOC_SLOT_REQUEST (0x1111) #define NCP_REQUEST (0x2222) #define NCP_DEALLOC_SLOT_REQUEST (0x5555) struct ncp_request_header { __u16 type __attribute__ ((packed)); __u8 sequence __attribute__ ((packed)); __u8 conn_low __attribute__ ((packed)); __u8 task __attribute__ ((packed)); __u8 conn_high __attribute__ ((packed)); __u8 function __attribute__ ((packed)); __u8 data[0] __attribute__ ((packed)); }; #define NCP_REPLY (0x3333) #define NCP_POSITIVE_ACK (0x9999) struct ncp_reply_header { __u16 type __attribute__ ((packed)); __u8 sequence __attribute__ ((packed)); __u8 conn_low __attribute__ ((packed)); __u8 task __attribute__ ((packed)); __u8 conn_high __attribute__ ((packed)); __u8 completion_code __attribute__ ((packed)); __u8 connection_state __attribute__ ((packed)); __u8 data[0] __attribute__ ((packed)); }; #define NCP_BURST_PACKET (0x7777) struct ncp_burst_header { __u16 type __attribute__ ((packed)); __u8 system_flags __attribute__ ((packed)); __u8 stream_type __attribute__ ((packed)); __u32 source_conn __attribute__ ((packed)); __u32 dest_conn __attribute__ ((packed)); __u32 packet_sequence __attribute__ ((packed)); __u32 send_delay __attribute__ ((packed)); __u16 burst_sequence __attribute__ ((packed)); __u16 ack_sequence __attribute__ ((packed)); __u32 burst_length __attribute__ ((packed)); __u32 data_offset __attribute__ ((packed)); __u16 data_bytes __attribute__ ((packed)); __u16 missing_frags __attribute__ ((packed)); }; void handle_ipx (unsigned char *buf, int length, char *frame, int no); void handle_ncp (struct sockaddr_ipx *source, struct sockaddr_ipx *target, unsigned char *buf, int length, int no); int handle_burst(struct sockaddr_ipx *source, struct sockaddr_ipx *target, unsigned char *buf, int length, int no); #define SAP_MAX_SERVER_NAME_LENGTH 48 /* in network packets */ #define SAP_MAX_SAPS_PER_PACKET 7 #define SAP_SHUTDOWN 16 /* Magic "hops" value to stop SAP advertising */ /* SAP Query structure (returned in sap_packet as an array) * NBO == Network Byte Order) */ typedef struct saps { __u16 serverType __attribute__ ((packed)); /* NBO */ __u8 serverName[SAP_MAX_SERVER_NAME_LENGTH] __attribute__ ((packed)); struct ipx_address serverAddress __attribute__ ((packed)); __u16 serverHops __attribute__ ((packed)); /* NBO */ } SAPS; /* General Service/Nearest Server Response SAP packet */ union sap_packet { unsigned short sapOperation; struct sap_query { __u16 sapOperation __attribute__ ((packed)); __u16 serverType __attribute__ ((packed)); } query; struct sap_response { __u16 sapOperation __attribute__ ((packed)); /* each SAP can has a max of SAP_MAX_SAPS_PER_PACKET packets */ SAPS sap[SAP_MAX_SAPS_PER_PACKET] __attribute__ ((packed)); } response; }; /* print out one SAP record */ static void print_sap(FILE *file, SAPS *sapp) { fprintf(file, " Name:%s, serverType 0x%x, ", sapp->serverName, ntohs(sapp->serverType)); ipx_fprint_network(file, ntohl(sapp->serverAddress.net)); fprintf(file, ":"); ipx_fprint_node(file, sapp->serverAddress.node); fprintf(file, ":"); ipx_fprint_port(file, ntohs(sapp->serverAddress.sock)); fprintf(file, " (Hops %d)\n", ntohs(sapp->serverHops)); } void handle_ipx (unsigned char *buf, int length, char *frame, int no) { struct ipx_packet *h = (struct ipx_packet *)buf; struct sockaddr_ipx s_addr; struct sockaddr_ipx d_addr; union sap_packet *sappacket; int hbo_dsock; /* Host Byte Order of Destination SOCKet */ int hbo_sapop; /* Host Byte Order of SAP OPeration */ memset(&s_addr, 0, sizeof(s_addr)); memset(&d_addr, 0, sizeof(d_addr)); memcpy(s_addr.sipx_node, h->ipx_source.node, sizeof(s_addr.sipx_node)); s_addr.sipx_port = h->ipx_source.sock; s_addr.sipx_network = h->ipx_source.net; memcpy(d_addr.sipx_node, h->ipx_dest.node, sizeof(d_addr.sipx_node)); d_addr.sipx_port = h->ipx_dest.sock; d_addr.sipx_network = h->ipx_dest.net; printf("%6.6d %s from ", no, frame); ipx_print_saddr(&s_addr); printf(" to "); ipx_print_saddr(&d_addr); printf("\n"); if (handle_burst(&s_addr, &d_addr, buf + sizeof(struct ipx_packet), length - sizeof(struct ipx_packet), no) != 0) { return; } if ( (ntohs(s_addr.sipx_port) == 0x451) || (ntohs(d_addr.sipx_port) == 0x451)) { handle_ncp(&s_addr, &d_addr, buf + sizeof(struct ipx_packet), length - sizeof(struct ipx_packet), no); } else /* next 3 handle IPX by type vs by socket (one or other) */ /* Note: most things use either ipx_type OR socket, not both */ if (h->ipx_type == 0x01) printf(" type 0x01 (RIP packet (router))\n"); else if (h->ipx_type == 0x05) printf(" type 0x05 (SPX sequenced packet)\n"); else if (h->ipx_type == 0x14) printf(" type 0x14 (propogated Client-NetBios)\n"); else { hbo_dsock = ntohs(d_addr.sipx_port); if (hbo_dsock == 0x452) /* SAP */ { sappacket = (union sap_packet *) (buf + sizeof(struct ipx_packet)); hbo_sapop = ntohs(sappacket->sapOperation); if ((hbo_sapop == 0x01) || (hbo_sapop == 0x03)) { printf(" type 0x%x, SAP op:0x%x %s Query, " "serverType 0x%x wanted\n", h->ipx_type, hbo_sapop, (hbo_sapop == 0x01)?"General Service" : (hbo_sapop == 0x03)?"Nearest Server" : "Error", ntohs(sappacket->query.serverType)); } else { int hops; hops = ntohs(sappacket-> response.sap[0].serverHops); printf(" type 0x%x, SAP op:0x%x %s %s\n", h->ipx_type, hbo_sapop, (hbo_sapop == 0x02) ? "General Service Response" : (hbo_sapop == 0x04) ? "Nearest Server Response" : "Unknown", (hops >= SAP_SHUTDOWN) ? "[Shutdown]" : ""); /* Service ending */ if (hops >= SAP_SHUTDOWN) { print_sap(stdout, sappacket->response.sap); } #ifdef DUMPALLSAPS /* If you want to dump all SAP's */ else { int num_saps; SAPS *sapp; num_saps = (length - sizeof(struct ipx_packet) - 2) / sizeof(SAPS); sapp = sappacket->response.sap; for(; num_saps > 0; sapp++, num_saps--) print_sap(stdout, sapp); } #endif /* DUMPALLSAPS */ } } else /* Other IPX types */ printf(" type 0x%x, Socket 0x%x (%s)\n", h->ipx_type, hbo_dsock, (hbo_dsock == 0x451) ? "NCP" : /* (hbo_dsock == 0x452) ? "SAP" :*/ (hbo_dsock == 0x453) ? "RIP" : (hbo_dsock == 0x455) ? "Client-NetBios" : (hbo_dsock == 0x456) ? "Diags" : (hbo_dsock == 0x002) ? "Xecho" : (hbo_dsock == 0x8063) ? "NVT2" : "Other"); } } int handle_burst(struct sockaddr_ipx *source, struct sockaddr_ipx *target, unsigned char *buf, int length, int no) { struct ncp_burst_header *rq = (struct ncp_burst_header *)buf; if (rq->type != NCP_BURST_PACKET) { return 0; } printf("Burst Packet\n"); printf("Stream Type: %02X, System Flags: %02X\n", rq->stream_type, rq->system_flags); printf("Source Conn: %08X, Dest Conn: %08X, Packet Seq: %08X\n", rq->source_conn, rq->dest_conn, (unsigned int)ntohl(rq->packet_sequence)); printf("Send Delay: %08X, Burst Seq: %04X, Ack Seq: %04X\n", (unsigned int)ntohl(rq->send_delay), ntohs(rq->burst_sequence), ntohs(rq->ack_sequence)); printf("Burst Length: %08X\n", (unsigned int)ntohl(rq->burst_length)); printf("Data Offset: %08X, Data Bytes: %04X, Missing Frags: %04X\n", (unsigned int)ntohl(rq->data_offset), ntohs(rq->data_bytes), ntohs(rq->missing_frags)); if (ntohs(rq->data_bytes) == 24) { struct ncp_burst_request { struct ncp_burst_header h __attribute__ ((packed)); __u32 function __attribute__ ((packed)); __u32 file_handle __attribute__ ((packed)); __u8 reserved[8] __attribute__ ((packed)); __u32 file_offset __attribute__ ((packed)); __u32 number_of_bytes __attribute__ ((packed)); } *brq = (struct ncp_burst_request *)rq; printf("Assuming Burst Request:\n"); printf("%s: Handle %08X, Offset %08X, Bytes %08X\n", brq->function == 1 ? "Read " : "Write", brq->file_handle, (unsigned int)ntohl(brq->file_offset), (unsigned int)ntohl(brq->number_of_bytes)); } printf("\n"); return 1; } void handle_ncp(struct sockaddr_ipx *source, struct sockaddr_ipx *target, unsigned char *buf, int length, int no) { struct ncp_request_header *rq = (struct ncp_request_header *)buf; struct ncp_reply_header *rs = (struct ncp_reply_header *)buf; unsigned char *data = NULL; int data_length = 0; int i; static struct sockaddr_ipx request_source; static struct ncp_request_header request_header; static char request_data[5]; if (ntohs(rq->type) == NCP_REQUEST) { /* Request */ printf("NCP request: conn: %-5d, seq: %-3d, task: %-3d, ", rq->conn_low + 256 * rq->conn_high, rq->sequence, rq->task); memcpy(&request_source, source, sizeof(request_source)); memcpy(&request_header, rq, sizeof(request_header)); memcpy(request_data, rq+1, sizeof(request_data)); data = buf + sizeof(struct ncp_request_header); data_length = length - sizeof(struct ncp_request_header); switch(rq->function) { case 18: printf("fn: %-3d\n", rq->function); printf("Get Volume Info with Number\n"); break; case 20: printf("fn: %-3d\n", rq->function); printf("Get File Server Date and Time\n"); break; case 21: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[2]); switch(data[2]) { case 0: printf("Send Broadcast Message\n"); break; case 1: printf("Get Broadcast Message\n"); break; } data += 3; data_length -= 3; break; case 22: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[2]); switch(data[2]) { case 00: printf("Set Directory Handle\n"); break; case 01: printf("Get Directory Path\n"); break; case 02: printf("Scan Directory Information\n"); break; case 03: printf("Get Effective Directory Rights\n"); break; case 05: printf("Get Volume Number\n"); break; case 06: printf("Get Volume Name\n"); break; case 10: printf("Create directory\n"); break; case 18: printf("Allocate Permanent Dir Handle\n"); break; case 20: printf("Deallocate Directory Handle\n"); break; case 21: printf("Get Volume Info with handle\n"); break; case 39: printf("Add ext. Trustee to Dir or File\n"); break; case 48: printf("Get Name Space Directory Entry\n"); break; } data += 3; data_length -= 3; break; case 23: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[2]); switch(data[2]) { case 17: printf("Get Fileserver Information\n"); break; case 22: printf("Get Station's logged Info (old)\n"); break; case 23: printf("Get Crypt Key\n"); break; case 24: printf("Encrypted Login\n"); break; case 28: printf("Get Connection Information\n"); break; case 50: printf("Create Bindery Object\n"); break; case 53: printf("Get Bindery Object ID\n"); break; case 54: printf("Get Bindery Object Name\n"); break; case 55: printf("Scan Bindery Object\n"); break; case 57: printf("Create Property\n"); break; case 59: printf("Change Property Security\n"); break; case 60: printf("Scan Property\n"); break; case 61: printf("Read Property Value\n"); break; case 62: printf("Write Property Value\n"); break; case 65: printf("Add Bindery Object to Set\n"); break; case 67: printf("Is Bindery Object in Set\n"); break; case 70: printf("Get Bindery Access Level\n"); break; case 72: printf("Get Bindery Object Access Level\n"); break; case 75: printf("Keyed change password\n"); break; case 113: printf("Service Queue Job (old)\n"); break; case 124: printf("Service Queue Job \n"); break; } data += 3; data_length -= 3; break; case 24: printf("fn: %-3d\n", rq->function); printf("End of Job\n"); break; case 33: printf("fn: %-3d\n", rq->function); printf("Negotiate Buffer size\n"); break; case 34: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[2]); data += 3; data_length -= 3; break; case 62: printf("fn: %-3d\n", rq->function); printf("File Search Initialize\n"); break; case 63: printf("fn: %-3d\n", rq->function); printf("File Search Continue\n"); break; case 64: printf("fn: %-3d\n", rq->function); printf("Search for a file\n"); break; case 66: printf("fn: %-3d\n", rq->function); printf("Close File\n"); break; case 67: printf("fn: %-3d\n", rq->function); printf("Create File\n"); break; case 72: printf("fn: %-3d\n", rq->function); printf("Read from File\n"); break; case 73: printf("fn: %-3d\n", rq->function); printf("Write to File\n"); break; case 75: printf("fn: %-3d\n", rq->function); printf("Set File Time Date Stamp\n"); break; case 76: printf("fn: %-3d\n", rq->function); printf("Open File (old)\n"); break; case 87: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[0]); switch(data[0]) { case 1: { unsigned char *p = &(data[0]); printf("Open Create File or Subdirectory\n"); printf("Name Space: %d\n", p[1]); printf("Open Create Mode: %x\n", p[2]); printf("Search Attributes: %x\n", *(__u16 *)&(p[3])); printf("Return Information Mask: %x\n", (unsigned int)(*(__u32 *)&(p[5]))); printf("Desired Access Rights: %x\n", *(__u16 *)&(p[9])); break; } case 2: printf("Initialize Search\n"); break; case 3: printf("Search for File or Subdirectory\n"); break; case 6: printf("Obtain File Or Subdirectory " "Information\n"); break; case 8: printf("Delete a File Or Subdirectory\n"); break; case 12: printf("Allocate Short Directory Handle\n"); break; } data += 1; data_length -= 1; break; case 97: { struct INPUT { __u16 proposed_max_size; __u8 security_flag; } *i = (struct INPUT *)data; printf("fn: %-3d\n", rq->function); printf("Get Big Packet NCP Max Packet Size\n"); printf("proposed_max_size: %x\n", ntohs(i->proposed_max_size)); printf("security_flag: %x\n", i->security_flag); break; } case 101: { struct INPUT { __u32 local_conn_id; __u32 local_max_packet_size; __u16 local_target_socket; __u32 local_max_send_size; __u32 local_max_recv_size; } *i = (struct INPUT *)data; printf("fn: %-3d\n", rq->function); printf("Packet Burst Connection Request\n"); printf("local_conn_id: %lx\n", ntohl(i->local_conn_id)); printf("local_max_packet_size: %lx\n", ntohl(i->local_max_packet_size)); printf("local_target_socket: %lx\n", ntohl(i->local_target_socket)); printf("local_max_send_size: %lx\n", ntohl(i->local_max_send_size)); printf("local_max_recv_size: %lx\n", ntohl(i->local_max_recv_size)); } break; case 104: { printf("fn: %-3d, subfn: %-3d, NDS call\n", rq->function, data[0]); /* I took this information from the (german!!) book 'Einf"uhrung in die NetWare LAN Analyse', Laura A. Chappell, Dan E. Hakes, Novell Press, Markt & Technik, ISBN 3-8272-5084-6, and from the book mentioned in the ncpfs README. I'm not sure it is correct, because I do not have NW 4.x. If you have the time, could you watch a NW4 workstation, and tell me whether anything of this makes sense at all. */ switch(data[0]) { case 1: printf("Ping for NDS\n"); break; case 2: { struct INPUT { __u8 subfunction_code; __u32 fragger_handle; __u32 max_fragment_size; __u32 message_size; __u32 fragment_flag; __u32 verb; } *i = (struct INPUT *)data; printf("Send NDS Fragment Request/Reply\n"); printf("fragger_handle: %lx\n", (unsigned long)i->fragger_handle); printf("max_fragment_size: %lx\n", (unsigned long)i->max_fragment_size); printf("message_size: %lx\n", (unsigned long)i->message_size); printf("fragment_flag: %lx\n", (unsigned long)i->fragment_flag); printf("verb: %d\n", i->verb); switch (i->verb) { case 1: printf("Resolve Name\n"); break; case 2: printf("Read Entry Information\n"); break; case 3: printf("Read\n"); break; case 4: printf("Compare\n"); break; case 5: printf("List\n"); break; case 6: printf("Search Entries\n"); break; case 7: printf("Add Entry\n"); break; case 8: printf("Remove Entry\n"); break; case 9: printf("Modify Entry\n"); break; case 10: printf("Modify RDN\n"); break; case 11: printf("Create Attribute\n"); break; case 12: printf("Read Attribute Definition\n"); break; case 13: printf("Remove Attribute Definition\n"); break; case 14: printf("Define Class\n"); break; case 15: printf("Read Class Definition\n"); break; case 16: printf("Modify Class Definition\n"); break; case 17: printf("Remove Class Definition\n"); break; case 18: printf("List Containable Classes\n"); break; case 19: printf("Get Effective Rights\n"); break; case 20: printf("Add Partition\n"); break; case 21: printf("Remove Partition\n"); break; case 22: printf("List Partitions\n"); break; case 23: printf("Split Partition\n"); break; case 24: printf("Join Partitions\n"); break; case 25: printf("Add Replica\n"); break; case 26: printf("Remove Replica\n"); break; case 27: printf("Open Stream\n"); break; case 28: printf("Search Filter\n"); break; case 29: printf("Create Subordinate Reference\n"); break; case 30: printf("Link Replica\n"); break; case 31: printf("Change Replica Type\n"); break; case 32: printf("Start Update Schema\n"); break; case 33: printf("End Update Schema\n"); break; case 34: printf("Update Schema\n"); break; case 35: printf("Start Update Replica\n"); break; case 36: printf("End Update Replica\n"); break; case 37: printf("Update Replica\n"); break; case 38: printf("Synchronize Partition\n"); break; case 39: printf("Synchronize Schema\n"); break; case 40: printf("Read Syntaxes\n"); break; case 41: printf("Get Replica Root ID\n"); break; case 42: printf("Begin Move Entry\n"); break; case 43: printf("Finish Move Entry\n"); break; case 44: printf("Release Moved Entry\n"); break; case 45: printf("Backup Entry\n"); break; case 46: printf("Restore Entry\n"); break; case 47: printf("Save DIB\n"); break; case 48: case 49: printf("Unused\n"); break; case 50: printf("Close Iteration\n"); break; case 51: printf("Unused\n"); break; case 52: printf("Audit Skulking\n"); break; case 53: printf("Get Server Address\n"); break; case 54: printf("Set Keys\n"); break; case 55: printf("Change Password\n"); break; case 56: printf("Verify Password\n"); break; case 57: printf("Begin Login\n"); break; case 58: printf("Finish Login\n"); break; case 59: printf("Begin Authentication\n"); break; case 60: printf("Finish Authentication\n"); break; case 61: printf("Logout\n"); break; case 62: printf("Repair Ring\n"); break; case 63: printf("Repair Timestamps\n"); break; case 64: printf("Create Back Link\n"); break; case 65: printf("Delete External Reference\n"); break; case 66: printf("Rename External Reference\n"); break; case 67: printf("Create Directory Entry\n"); break; case 68: printf("Remove Directory Entry\n"); break; case 69: printf("Designate New Master\n"); break; case 70: printf("Change Tree Name\n"); break; case 71: printf("Partition Entry Count\n"); break; case 72: printf("Check Login Restrictions\n"); break; case 73: printf("Start Join\n"); break; case 74: printf("Low Level Split\n"); break; case 75: printf("Low Level Join\n"); break; case 76: printf("Abort Low Level Join\n"); break; case 77: printf("Get All Servers\n"); break; default: printf("Unknown Verb: %d\n", data[0]); break; } break; } case 3: printf("Close NDS Fragment\n"); break; case 4: printf("Return Bindery Context\n"); break; case 5: printf("Monitor NDS connection\n"); break; case 200: printf("NDS Auditing\n"); break; default: break; } } default: printf("fn: %-3d\n", rq->function); } } if (ntohs(rs->type) == NCP_REPLY) { printf("NCP respons: conn: %-5d, seq: %-3d, task: %-3d, ", rs->conn_low + 256 * rs->conn_high, rs->sequence, rs->task); printf("compl: %-3d, conn_st: %-3d\n", rs->completion_code, rs->connection_state); data = buf + sizeof(struct ncp_reply_header); data_length = length - sizeof(struct ncp_reply_header); if ( (memcmp(&request_source, target, sizeof(request_source)) == 0) && (request_header.sequence == rs->sequence)) { switch (request_header.function) { case 22: switch (request_data[2]) { case 18: { struct XDATA { __u8 new_directory_handle; __u8 access_rights_mask; } *x = (struct XDATA *)data; printf("new_directory_handle: %x\n", x->new_directory_handle); printf("access_rights_mask: %x\n", x->access_rights_mask); } break; } break; case 72: printf("Read data\n"); data_length = 0; break; case 97: { struct XDATA { __u16 accepted_max_size; __u16 echo_socket; __u8 security_flag; } *x = (struct XDATA *)data; printf("accepted_max_size: %x\n", ntohs(x->accepted_max_size)); printf("echo_socket: %x\n", ntohs(x->echo_socket)); printf("security_flag: %x\n", (x->security_flag)); } break; case 101: { struct XDATA { __u8 completion_code; __u32 remote_target_id; __u32 remote_max_packet_size; } *x = (struct XDATA *)data; printf("completion_code: %x\n", x->completion_code); printf("remote_target_id: %lx\n", ntohl(x->remote_target_id)); printf("remote_max_packet_size: %lx\n", ntohl(x->remote_max_packet_size)); } break; } } } if (data == NULL) { data = buf; data_length = length; } i = 0; while (i < data_length) { int j; for (j = i; j < i+16; j++) { if (j >= data_length) { printf(" "); } else { printf("%-2.2X", data[j]); } } printf(" "); for (j = i; j < i+16; j++) { if (j >= data_length) { break; } if (isprint(data[j])) { printf("%c", data[j]); } else { printf("."); } } printf("\n"); i += 16; } printf("\n"); } void main (int argc, char *argv[]) { unsigned char buf[16384]; unsigned char packet[8192]; unsigned char *b; int len; int i = 1; while (fgets(buf, sizeof(buf), stdin) != NULL) { if (strlen(buf) == sizeof(buf)-1) { fprintf(stderr, "line too long\n"); exit(1); } b = strchr(buf, ' '); if (b == NULL) { fprintf(stderr, "illegal line format\n"); exit(1); } *b = '\0'; b += 1; len = 0; while ((b[0] != '\0') && (b[1] != '\0')) { unsigned int value; if (sscanf(b, "%2x", &value) != 1) { fprintf(stderr, "illegal packet\n"); exit(1); } packet[len] = value; b += 2; len += 1; } handle_ipx(packet, len, buf, i); i += 1; } exit (0); }