📄 ars.c
字号:
/* Copyright (C) 2000,2001 Salvatore Sanfilippo <antirez@invece.org> * See the LICENSE file for more information. * * TODO: * o Functions to add addresses and timestamps for some IP and TCP option * o IGMP support * o DNS support * o ARS add_build_layer() facility and Co., read the PROPOSAL file. *//* $Id: ars.c,v 1.4 2003/07/28 09:00:54 njombart Exp $ */#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <string.h>#include <sys/types.h>#ifndef WIN32#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#else#include <winsock2.h>#include <ws2tcpip.h>#endif#include <unistd.h>#include "ars.h"/* prototypes */int ars_compiler_ip(struct ars_packet *pkt, int layer);int ars_compiler_ipopt(struct ars_packet *pkt, int layer);int ars_compiler_tcp(struct ars_packet *pkt, int layer);int ars_compiler_tcpopt(struct ars_packet *pkt, int layer);int ars_compiler_udp(struct ars_packet *pkt, int layer);int ars_compiler_icmp(struct ars_packet *pkt, int layer);int ars_compiler_abort(struct ars_packet *pkt, int layer) { return 0; }/* Initialize a packets context: * must be called before to work with the packet's layers */int ars_init(struct ars_packet *pkt){ int j; pkt->p_error = NULL; pkt->p_layer_nr = 0; for (j = 0; j < ARS_MAX_LAYER; j++) { pkt->p_layer[j].l_size = 0; pkt->p_layer[j].l_flags = 0; pkt->p_layer[j].l_type = ARS_TYPE_NULL; pkt->p_layer[j].l_data = NULL; pkt->p_layer[j].l_packet = pkt; } for (j = 0; j < ARS_TYPE_SIZE; j++) pkt->p_default[j] = NULL; return -ARS_OK;}/* Destroy (free the allocated memory) a packet context */int ars_destroy(struct ars_packet *pkt){ int j; free(pkt->p_error); for (j = 0; j < ARS_MAX_LAYER; j++) { if (pkt->p_layer[j].l_type != ARS_TYPE_NULL && pkt->p_layer[j].l_data != NULL) free(pkt->p_layer[j].l_data); } return ars_init(pkt); /* Re-initialize it */}/* THe out of memory message must be statically allocated */char *ars_error_nomem = "Out of memory";/* Set the error description */int ars_set_error(struct ars_packet *pkt, char *error){ if (pkt == NULL) return -ARS_OK; free(pkt->p_error); /* p_error is initialized to NULL */ if ((pkt->p_error = strdup(error)) == NULL) { /* To put the error description for the -KO_NOMEM * error we needs a statically allocated error message: * Note that all other functions don't need to report * a statically allocated error message for -KO_NOMEM * it will be auto-selected if strdup() returns NULL */ pkt->p_error = ars_error_nomem; } return -ARS_OK; /* report anyway success */}/* Set the default for a layer */int ars_set_default(struct ars_packet *pkt, int layer_type, void *def){ pkt->p_default[layer_type] = def; return -ARS_OK;}/* return nonzero if the packet is full */int ars_nospace(struct ars_packet *pkt){ return (pkt->p_layer_nr == ARS_MAX_LAYER);}/* Check if the layer number is valid */int ars_valid_layer(int layer){ if (layer < 0 || layer >= ARS_MAX_LAYER) return -ARS_INVALID; return -ARS_OK;}/* Add an a generic layer */int ars_add_generic(struct ars_packet *pkt, size_t size, int type){ int layer; if (ars_nospace(pkt)) { ars_set_error(pkt, "No space for the next layer"); return -ARS_NOSPACE; } layer = pkt->p_layer_nr; /* You may want to create a 0 len layer and then realloc */ if (size != 0) { pkt->p_layer[layer].l_data = malloc(size); if (pkt->p_layer[layer].l_data == NULL) { ars_set_error(pkt, "Out of memory adding a new layer"); return -ARS_NOMEM; } memset(pkt->p_layer[layer].l_data, 0, size); /* Copy the default if any */ if (pkt->p_default[type] != NULL) { memcpy(pkt->p_layer[layer].l_data, pkt->p_default[type], size); } } pkt->p_layer[layer].l_type = type; pkt->p_layer[layer].l_size = size; return -ARS_OK;}/* Add an IP layer */void *ars_add_iphdr(struct ars_packet *pkt, int unused){ int retval; retval = ars_add_generic(pkt, sizeof(struct ars_iphdr), ARS_TYPE_IP); if (retval != -ARS_OK) return NULL; pkt->p_layer_nr++; return pkt->p_layer[pkt->p_layer_nr-1].l_data;}/* Add on IP option */void *ars_add_ipopt(struct ars_packet *pkt, int option){ int retval; struct ars_ipopt *ipopt; int opt_len; switch(option) { case ARS_IPOPT_END: case ARS_IPOPT_NOOP: opt_len = 1; break; case ARS_IPOPT_SEC: opt_len = 11; break; case ARS_IPOPT_SID: opt_len = 4; break; case ARS_IPOPT_LSRR: case ARS_IPOPT_SSRR: case ARS_IPOPT_RR: case ARS_IPOPT_TIMESTAMP: /* We allocate the max (40 bytes) but the real layer size * may be modified by ars_ipopt_set*() functions */ opt_len = 40; break; default: return NULL; /* Unsupported option */ break; } retval = ars_add_generic(pkt, opt_len, ARS_TYPE_IPOPT); if (retval != -ARS_OK) return NULL; ipopt = pkt->p_layer[pkt->p_layer_nr].l_data; pkt->p_layer_nr++; ipopt->kind = option; ipopt->len = opt_len; /* the default, can be modified inside switch() */ /* Perform some special operation for some option */ switch(option) { case ARS_IPOPT_LSRR: /* ars_ipopt_setls() will change some field */ case ARS_IPOPT_SSRR: /* ars_ipopt_setss() will change some field */ case ARS_IPOPT_RR: /* ars_ipopt_setrr() will change some field */ /* RFC 791 needs the roomlen - 3 octects, so the gateways * can compare len and ptr to check for room. * Try to break this to stress lame TCP/IP implementation */ ipopt->len = opt_len - 2 - 3; ipopt->un.rr.ptr = 4; break; case ARS_IPOPT_TIMESTAMP: ipopt->un.tstamp.ptr = 5; ipopt->un.tstamp.flags = ARS_IPOPT_TS_TSONLY; /* default */ break; } return ipopt;}/* Add a UDP layer */void *ars_add_udphdr(struct ars_packet *pkt, int unused){ int retval; retval = ars_add_generic(pkt, sizeof(struct ars_udphdr), ARS_TYPE_UDP); if (retval != -ARS_OK) return NULL; pkt->p_layer_nr++; return pkt->p_layer[pkt->p_layer_nr-1].l_data;}/* Add a TCP layer */void *ars_add_tcphdr(struct ars_packet *pkt, int unused){ int retval; retval = ars_add_generic(pkt, sizeof(struct ars_tcphdr), ARS_TYPE_TCP); if (retval != -ARS_OK) return NULL; pkt->p_layer_nr++; return pkt->p_layer[pkt->p_layer_nr-1].l_data;}/* Add TCP options */void *ars_add_tcpopt(struct ars_packet *pkt, int option){ int retval; struct ars_tcpopt *tcpopt; int opt_len; switch(option) { case ARS_TCPOPT_NOP: case ARS_TCPOPT_EOL: opt_len = 1; break; case ARS_TCPOPT_MAXSEG: opt_len = 4; break; case ARS_TCPOPT_WINDOW: opt_len = 3; break; case ARS_TCPOPT_SACK_PERM: /* ars_tcpopt_setsack() must change this */ case ARS_TCPOPT_SACK: opt_len = 2; break; case ARS_TCPOPT_ECHOREQUEST: case ARS_TCPOPT_ECHOREPLY: opt_len = 6; break; case ARS_TCPOPT_TIMESTAMP: opt_len = 10; break; default: return NULL; /* Unsupported option */ break; } retval = ars_add_generic(pkt, opt_len, ARS_TYPE_TCPOPT); if (retval != -ARS_OK) return NULL; tcpopt = pkt->p_layer[pkt->p_layer_nr].l_data; pkt->p_layer_nr++; tcpopt->kind = option; /* EOL and NOP lacks the len field */ if (option != ARS_TCPOPT_EOL && option != ARS_TCPOPT_NOP) tcpopt->len = opt_len; /* Perform some special operation for the option */ switch(option) { case ARS_TCPOPT_ECHOREQUEST: case ARS_TCPOPT_ECHOREPLY: memset(tcpopt->un.echo.info, 0, 4); break; case ARS_TCPOPT_TIMESTAMP: memset(tcpopt->un.timestamp.tsval, 0, 4); memset(tcpopt->un.timestamp.tsecr, 0, 4); break; } return tcpopt;}/* Add an ICMP layer */void *ars_add_icmphdr(struct ars_packet *pkt, int unused){ int retval; struct ars_icmphdr *icmp; retval = ars_add_generic(pkt, sizeof(struct ars_icmphdr),ARS_TYPE_ICMP); if (retval != -ARS_OK) return NULL; icmp = pkt->p_layer[pkt->p_layer_nr].l_data; pkt->p_layer_nr++; return (struct ars_icmphdr*) pkt->p_layer[pkt->p_layer_nr-1].l_data;}/* Add data, for IP-RAW, TCP, UDP, and so on */void *ars_add_data(struct ars_packet *pkt, int size){ int retval; void *boguspointer = "zzappt"; /* we can't return NULL for size == 0 */ if (size < 0) { ars_set_error(pkt, "Tryed to add a DATA layer with size < 0"); return NULL; } retval = ars_add_generic(pkt, size, ARS_TYPE_DATA); if (retval != -ARS_OK) return NULL; pkt->p_layer_nr++; if (size > 0) return pkt->p_layer[pkt->p_layer_nr-1].l_data; else return boguspointer;}/* Remove a layer */int ars_remove_layer(struct ars_packet *pkt, int layer){ if (layer == ARS_LAST_LAYER) layer = pkt->p_layer_nr -1; if (ars_valid_layer(layer) != -ARS_OK) return -ARS_INVALID; free(pkt->p_layer[layer].l_data); /* No problem if it's NULL */ pkt->p_layer[layer].l_type = ARS_TYPE_NULL; pkt->p_layer[layer].l_size = 0; pkt->p_layer[layer].l_flags = 0; pkt->p_layer[layer].l_data = NULL; pkt->p_layer[layer].l_packet = pkt; return -ARS_OK;}/* Return the sum of the size of the specifed layer and of all the * following layers */size_t ars_relative_size(struct ars_packet *pkt, int layer_nr){ int j = layer_nr, rel_size = 0; while (j < ARS_MAX_LAYER && pkt->p_layer[j].l_type != ARS_TYPE_NULL) { rel_size += pkt->p_layer[j].l_size; j++; } return rel_size;}/* Just a short cut for ars_relative_size(), to get the total size */size_t ars_packet_size(struct ars_packet *pkt){ return ars_relative_size(pkt, 0);}/* from R. Stevens's Network Programming */u_int16_t ars_cksum(void *vbuf, size_t nbytes){ u_int16_t *buf = (u_int16_t*) vbuf; u_int32_t sum; u_int16_t oddbyte; sum = 0; while (nbytes > 1) { sum += *buf++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; *((u_int16_t *) &oddbyte) = *(u_int8_t *) buf; sum += oddbyte; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (u_int16_t) ~sum;}/* Multiple buffers checksum facility */u_int16_t ars_multi_cksum(struct mc_context *c, int op, void *vbuf, size_t nbytes){ u_int16_t *buf = (u_int16_t*) vbuf; u_int32_t sum; u_int16_t oddbyte; void *tmp; if (op == ARS_MC_INIT) { c->oddbyte_flag = 0; c->old = 0; return -ARS_OK; } else if (op == ARS_MC_UPDATE) { if (c->oddbyte_flag) { u_int8_t *x = (u_int8_t*)&oddbyte; oddbyte = 0; *((u_int16_t *) &oddbyte) = c->oddbyte << 8; *((u_int16_t *) &oddbyte) |= *(u_int8_t *) buf; oddbyte = (x[0] << 8) | x[1]; /* fix endianess */ c->old += oddbyte; nbytes--; c->oddbyte_flag = 0; /* We need to stay aligned -- bad slowdown, fix? */ tmp = alloca(nbytes); memcpy(tmp, vbuf+1, nbytes); buf = tmp; } sum = c->old; while (nbytes > 1) { sum += *buf++; nbytes -= 2; } c->old = sum; if (nbytes == 1) { c->oddbyte = *(u_int8_t*) buf; c->oddbyte_flag++; } return -ARS_OK; } else if (op == ARS_MC_FINAL) { sum = c->old; if (c->oddbyte_flag == 1) { oddbyte = 0; *((u_int16_t *) &oddbyte) = c->oddbyte; sum += oddbyte; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (u_int16_t) ~sum; } else { assert("else reached in ars_multi_cksum()" == ""); } return 0; /* unreached, here to prevent warnings */}/* The ARS compiler table is just a function pointers array: * For example to select the right function to compile an IP * layer use: ars_compiler[ARS_TYPE_IP](pkt, layer); * You can, of course, add your protocols and compilers: * * WARNING: take it syncronized with ars.h ARS_TYPE_* defines */struct ars_layer_info ars_linfo[ARS_TYPE_SIZE] = { /* NAME COMPILER ID * * ---- -------- -- */ { "NULL", ars_compiler_abort, 0 }, { "IP", ars_compiler_ip, 1 }, { "IPOPT", ars_compiler_ipopt, 2 }, { "ICMP", ars_compiler_icmp, 3 }, { "UDP", ars_compiler_udp, 4 },
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -