int set_raw_filter(unsigned int loc_idx, char *filter) { struct bpf_program raw_filter; //uint16_t snaplen = 65535; int linktype; //struct pcap_t *aa; int fd = -1; LERR("APPLY FILTER [%d]\n", loc_idx); if(loc_idx >= MAX_SOCKETS || sniffer_proto[loc_idx] == NULL) return 0; fd = pcap_get_selectable_fd(sniffer_proto[loc_idx]); linktype = profile_socket[loc_idx].link_type ? profile_socket[loc_idx].link_type : DLT_EN10MB; if (pcap_compile_nopcap(profile_socket[loc_idx].snap_len ? profile_socket[loc_idx].snap_len : 0xffff, linktype, &raw_filter, filter, 1, 0) == -1) { LERR("Failed to compile filter '%s'", filter); return -1; } #if ( defined (OS_LINUX) || defined (OS_SOLARIS) ) if(setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &raw_filter, sizeof(raw_filter)) < 0 ) { LERR(" setsockopt filter: [%s] [%d]", strerror(errno), errno); return -1; } #endif //free(BPF_code); pcap_freecode( (struct bpf_program *) &raw_filter); return 1; }
int send_data (void *buf, unsigned int len, unsigned int idx) { /* send this packet out of our socket */ void * p = buf; if(!profile_transport[idx].usessl) { size_t sendlen = send(profile_transport[idx].socket, p, len, 0); if(sendlen == -1) { if(errno == ECONNRESET) return -2; else if(errno == ECONNRESET) return -3; LERR("JSON send error: [%d]", errno); return -1; } } #ifdef USE_SSL else { if(SSL_write(profile_transport[idx].ssl, buf, len) < 0) { LERR("json: couldn't re-init ssl socket: [%d]", errno); return -1; } } #endif stats.send_packets_total++; /* RESET ERRORS COUNTER */ return 0; }
/* parse DIAMETER */ int w_parse_diameter_to_json(msg_t *msg) { int json_len; char json_diameter_buffer[JSON_BUFFER_LEN] = {0}; msg->mfree = 0; // call dissector if((json_len = parse_diameter((char *) msg->data, msg->len, json_diameter_buffer, JSON_BUFFER_LEN)) > 0) { /* msg->rcinfo.proto_type = rtcp_proto_type; */ msg->data = json_diameter_buffer; // JSON buff --> Msg data msg->len = json_len; msg->mfree = 1; } else { LERR("Error on parameters (data or length)\n"); if(msg->corrdata) { free(msg->corrdata); msg->corrdata = NULL; } return -1; } LERR("JSON DIAMETER %s\n", json_diameter_buffer); return 0; }
int init_hepsocket_blocking (void) { int s; struct timeval tv; fd_set myset; if(sock) close(sock); if ((s = getaddrinfo(capt_host, capt_port, hints, &ai)) != 0) { LERR( "capture: getaddrinfo: %s\n", gai_strerror(s)); return 2; } if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { LERR("Sender socket creation failed: %s\n", strerror(errno)); return 1; } if (connect(sock, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == -1) { select(sock + 1 , NULL, &myset, NULL, &tv); if (errno != EINPROGRESS) { LERR("Sender socket creation failed: %s\n", strerror(errno)); return 1; } } return 0; }
int send_data (void *buf, unsigned int len) { /* send this packet out of our socket */ int r = 0; void * p = buf; int sentbytes = 0; if(!usessl) { /* size_t sendlen = send(sock, p, len, 0); if(sendlen == -1) { LDEBUG("send error\n"); return -1; } sendPacketsCount++; */ /* size_t sendlen = len < 1024 ? len : 1024; size_t remlen = len; const void *curpos = buf; LDEBUG("SENDING!!!!!!!!!!!\n"); while (remlen > 0) { ssize_t len = send(sock, curpos, sendlen, MSG_NOSIGNAL); if (len == -1) return -1; curpos += len; remlen -= len; sendlen = (remlen < 1024) ? remlen : 1024; } */ while (sentbytes < len){ if( (r = send(sock, p, len - sentbytes, MSG_NOSIGNAL )) == -1) { LERR("send error\n"); return -1; } if (r != len - sentbytes) LDEBUG("send:multiple calls: %d\n", r); sentbytes += r; p += r; } sendPacketsCount++; } #ifdef USE_SSL else { if(SSL_write(ssl, buf, len) < 0) { LERR("capture: couldn't re-init ssl socket\r\n"); return -1; } sendPacketsCount++; } #endif /* RESET ERRORS COUNTER */ return 0; }
int send_hep_basic (rc_info_t *rcinfo, unsigned char *data, unsigned int len) { unsigned char *zipData = NULL; int sendzip = 0; #ifdef USE_ZLIB int status = 0; unsigned long dlen; if(pl_compress && hep_version == 3) { //dlen = len/1000+len*len+13; dlen = compressBound(len); zipData = malloc(dlen); /* give a little bit memmory */ /* do compress */ status = compress( zipData, &dlen, data, len ); if( status != Z_OK ){ LERR( "data couldn't be compressed\n"); sendzip = 0; if(zipData) free(zipData); /* release */ } else { sendzip = 1; len = dlen; } } #endif /* USE_ZLIB */ switch(hep_version) { case 3: return send_hepv3(rcinfo, sendzip ? zipData : data , len, sendzip); break; case 2: case 1: return send_hepv2(rcinfo, data, len); break; default: LERR( "Unsupported HEP version [%d]\n", hep_version); break; } if(zipData) free(zipData); return 0; }
bool SymbolJudger::loadSymbols(const char *filename) { if (m_loaded) return 1; m_loaded = 1; if (trie == NULL) { LE("trie is NULL. Failed to load symbal"); return 0; } FILE *fp = fopen(filename, "r"); if (!fp) { LERR("open file %s failed!", filename); return 0; } char s[LEN]; while (fgets(s, LEN, fp)) { int len = strlen(s); s[--len] = '\0'; bool ret = trie->insert(s, len, 1); if (!ret) return 0; } return 1; }
int init_socket(unsigned int loc_idx) { struct sockaddr_in v4addr; int status; status = uv_udp_init(loop,&udp_servers[loc_idx]); #if UV_VERSION_MAJOR == 0 v4addr = uv_ip4_addr(profile_socket[loc_idx].host, atoi(profile_socket[loc_idx].port)); #else status = uv_ip4_addr(profile_socket[loc_idx].host, atoi(profile_socket[loc_idx].port), &v4addr); #endif #if UV_VERSION_MAJOR == 0 status = uv_udp_bind(&udp_servers[loc_idx], v4addr,0); #else status = uv_udp_bind(&udp_servers[loc_idx], (struct sockaddr*)&v4addr, UV_UDP_REUSEADDR); #endif if(status < 0) { LERR( "capture: bind error"); return 2; } udp_servers[loc_idx].data = (void *) &loc_idx; status = uv_udp_recv_start(&udp_servers[loc_idx], on_alloc, on_recv); return 0; }
int w_clog(msg_t *_m, char *param1, char* param2) { if(param1[0] == 'E' || param1[0] == 'e') LERR("%s\n", param2); else if(param1[0] == 'N' || param1[0] == 'n') LNOTICE("%s\n", param2); else LDEBUG("%s\n", param2); return 1; }
void select_loop (void) { int n = 0; int initfails = 0; fd_set readfd; time_t prevtime = time(NULL); FD_ZERO(&readfd); FD_SET(sock, &readfd); while (1){ if (select(sock+1, &readfd, 0, 0, NULL) < 0){ LERR("select failed\n"); handler(1); } if (FD_ISSET(sock, &readfd)){ ioctl(sock, FIONREAD, &n); if (n == 0){ /* server disconnected*/ if(!usessl) { if(init_hepsocket()) initfails++; } #ifdef USE_SSL else { if(initSSL()) initfails++; } #endif /* USE_SSL */ if (initfails > 10) { time_t curtime = time (NULL); if (curtime - prevtime < 2){ pthread_mutex_lock(&lock); LERR( "HEP server is down... retrying after sleep...\n"); sleep(2); pthread_mutex_unlock(&lock); } initfails=0; prevtime = curtime; } } } } }
int init_jsonsocket_blocking (unsigned int idx) { int s, ret = 0; struct addrinfo *ai; struct addrinfo hints[1] = {{ 0 }}; stats.reconnect_total++; hints->ai_flags = AI_NUMERICSERV; hints->ai_family = AF_UNSPEC; if(!strncmp(profile_transport[idx].capt_proto, "udp", 3)) { hints->ai_socktype = SOCK_DGRAM; hints->ai_protocol = IPPROTO_UDP; } else if(!strncmp(profile_transport[idx].capt_proto, "tcp", 3) || !strncmp(profile_transport[idx].capt_proto, "ssl", 3)) { hints->ai_socktype = SOCK_STREAM; hints->ai_protocol = IPPROTO_TCP; } if(profile_transport[idx].socket) close(profile_transport[idx].socket); if ((s = getaddrinfo(profile_transport[idx].capt_host, profile_transport[idx].capt_port, hints, &ai)) != 0) { LERR( "capture: getaddrinfo: %s", gai_strerror(s)); return 2; } if((profile_transport[idx].socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { LERR("Sender socket creation failed: %s", strerror(errno)); return 1; } if ((ret = connect(profile_transport[idx].socket, ai->ai_addr, (socklen_t)(ai->ai_addrlen))) == -1) { //select(profile_transport[idx].socket + 1 , NULL, &myset, NULL, &tv); if (errno != EINPROGRESS) { LERR("Sender socket creation failed: %s", strerror(errno)); return 1; } } return 0; }
/* check if it's a diameter packet */ int w_is_diameter(msg_t *msg) { int ret; ret = is_diameter(msg->data, msg->len); switch(ret) { case -1: { LERR("Error on parameters (data or length)\n"); return -1; } case -2: { LERR("Error: NO DIAMETER packet found\n"); return -2; } } return ret; // 0 TRUE }
void showCerts(SSL* ssl) { X509 *cert; char *line; cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */ if ( cert != NULL ) { LERR("Server certificates:\n"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); LERR("Subject: %s\n", line); free(line); /* free the malloc'ed string */ line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); LERR("Issuer: %s\n", line); free(line); /* free the malloc'ed string */ X509_free(cert); /* free the malloc'ed certificate copy */ } else LERR("No certificates.\n"); }
int bind_protocol_tcp(protocol_tcp_api_t* api) { if (!api) { LERR("Invalid parameter value\n"); return -1; } api->lookup = hashapi_lookup; return 0; }
static uint8_t *extract_from_m2pa(msg_t *msg, size_t *len) { uint8_t *data; uint32_t data_len; if (msg->len < 8) { LERR("M2PA hdr too short %u", msg->len); return NULL; } data = msg->data; /* check the header */ if (data[0] != 0x01) { LERR("M2PA unknown version number %d", data[0]); return NULL; } if (data[1] != 0x00) { LERR("M2PA unknown reserved fields %d", data[1]); return NULL; } if (data[2] != M2PA_CLASS) { LDEBUG("M2PA unhandled message class %d", data[2]); return NULL; } if (data[3] != M2PA_DATA) { LDEBUG("M2PA not data msg but %d", data[3]); return NULL; } /* check the length */ memcpy(&data_len, &data[4], sizeof(data_len)); data_len = ntohl(data_len); if (msg->len < data_len) { LERR("M2PA data can't fit %u vs. %u", msg->len, data_len); return NULL; } /* skip the header */ data += 8; data_len -= 8; /* BSN, FSN and then priority */ if (data_len < 8) { LERR("M2PA no space for BSN/FSN %u\n", data_len); return NULL; } data += 8; data_len -= 8; if (data_len == 0) return NULL; else if (data_len < 1) { LERR("M2PA no space for prio %u\n", data_len); return NULL; } data += 1; data_len -= 1; *len = data_len; return data; }
int light_parse_sip(msg_t *msg) { int ret = -1; uint32_t bytes_parsed = 0; stats.received_packets_total++; memset(&msg->sip, 0, sizeof(sip_msg_t)); msg->sip.hasSdp = FALSE; msg->sip.hasTo = FALSE; msg->sip.hasPid = FALSE; msg->sip.hasFrom = FALSE; msg->sip.hasRuri = FALSE; msg->sip.hasToTag = FALSE; /* check if this is real SIP */ if (!isalpha(((char * )msg->data)[0])) { return -1; } msg->rcinfo.proto_type = PROTO_SIP; msg->parsed_data = NULL; if (!light_parse_message(msg->data, msg->len, &bytes_parsed, &msg->sip)) { LERR("bad parsing"); return -1; } if (msg->sip.callId.len == 0) { LERR("sipPacket CALLID has 0 len"); return -1; } stats.send_packets++; return ret; }
int w_send_json_api(msg_t *_m, char *param1) { int ret = -1; _m->profile_name = param1; LERR("SEND_JSON_API: [%s]\n", param1); ret = send_json(_m); return ret; }
int parse_packet(msg_t *msg, sip_msg_t *sipPacket, unsigned int type) { uint32_t bytes_parsed = 0; LDEBUG("SIP: [%.*s]\n", msg->len, msg->data); if (!parse_message(msg->data, msg->len, &bytes_parsed, sipPacket, type)) { LERR("bad parsing"); return 0; } if (sipPacket->callId.len == 0) { LERR("sipPacket CALLID has 0 len"); return 0; } if(sipPacket->hasVqRtcpXR) { msg->rcinfo.correlation_id.s = sipPacket->rtcpxr_callid.s; msg->rcinfo.correlation_id.len = sipPacket->rtcpxr_callid.len; } return 1; }
int check_rtcp_version (char *packet, int len) { if(packet == NULL || len == 0) return -1; rtcp_header_t *rtcp = (rtcp_header_t *)packet; if(rtcp->version != 2) { LERR("wrong version\n"); return -2; } return 1; }
void clear_ipports() { struct ipport_items *s, *tmp; if (pthread_rwlock_wrlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } /* free the hash table contents */ HASH_ITER(hh, ipports, s, tmp) { HASH_DEL(ipports, s); free(s); }
int load_module_xml_config() { char module_config_name[500]; xml_node *next; int i = 0; snprintf(module_config_name, 500, "%s/%s.xml", global_config_path, module_name); if ((module_xml_config = xml_parse(module_config_name)) == NULL) { LERR("Unable to open configuration file: %s", module_config_name); return -1; } /* check if this module is our */ next = xml_get("module", module_xml_config, 1); if (next == NULL) { LERR("wrong config for module: %s", module_name); return -2; } for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (strncmp(next->attr[i + 1], module_name, strlen(module_name))) { return -3; } } else if (!strncmp(next->attr[i], "serial", 6)) { module_serial = atol(next->attr[i + 1]); } else if (!strncmp(next->attr[i], "description", 11)) { module_description = next->attr[i + 1]; } } return 1; }
struct ipport_items *find_ipport_key(char *key) { struct ipport_items *ipport = NULL; int ret = 0; if (pthread_rwlock_rdlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } HASH_FIND_STR( ipports, key, ipport); pthread_rwlock_unlock(&ipport_lock); return ipport; }
int sigPipe(void) { struct sigaction new_action; /* sigation structure */ new_action.sa_handler = handlerPipe; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; if (sigaction(SIGPIPE, &new_action, NULL) == -1) { LERR("Failed to set new Handle"); return -1; } return 1; }
int parse_sip(msg_t *msg, unsigned int type) { int ret = -1; stats.received_packets_total++; memset(&msg->sip, 0, sizeof(sip_msg_t)); msg->sip.hasSdp = FALSE; msg->sip.hasTo = FALSE; msg->sip.hasPid = FALSE; msg->sip.hasFrom = FALSE; msg->sip.hasRuri = FALSE; msg->sip.hasToTag = FALSE; msg->sip.validMessage = FALSE; /* check if this is real SIP */ if (!isalpha(((char * )msg->data)[0])) { return -1; } msg->rcinfo.proto_type = PROTO_SIP; msg->parsed_data = NULL; if (parse_packet(msg, &msg->sip, type)) { ret = 1; msg->sip.validMessage = TRUE; stats.parsed_packets++; } else { LERR("SIP PARSE ERROR [%d]\n", ret); goto error; } stats.send_packets++; return ret; error: return -1; }
int initSSL(void) { long ctx_options; /* if(ssl) SSL_free(ssl); if(ctx) SSL_CTX_free(ctx); */ if(init_hepsocket_blocking()) { LERR("capture: couldn't init hep socket\r\n"); return 1; } ctx = initCTX(); /* workaround bug openssl */ ctx_options = SSL_OP_ALL; ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; SSL_CTX_set_options(ctx, ctx_options); /*extra*/ SSL_CTX_ctrl(ctx, BIO_C_SET_NBIO, 1, NULL); /* create new SSL connection state */ ssl = SSL_new(ctx); SSL_set_connect_state(ssl); /* attach socket */ SSL_set_fd(ssl, sock); /* attach the socket descriptor */ /* perform the connection */ if ( SSL_connect(ssl) == -1 ) { ERR_print_errors_fp(stderr); return 1; } showCerts(ssl); return 0; }
int initSSL(unsigned int idx) { long ctx_options; /* if(ssl) SSL_free(ssl); if(ctx) SSL_CTX_free(ctx); */ if(init_jsonsocket_blocking(idx)) { LERR("capture: couldn't init hep socket"); return 1; } profile_transport[idx].ctx = initCTX(); /* workaround bug openssl */ ctx_options = SSL_OP_ALL; ctx_options |= SSL_OP_NO_SSLv2; SSL_CTX_set_options(profile_transport[idx].ctx, ctx_options); /*extra*/ SSL_CTX_ctrl(profile_transport[idx].ctx, BIO_C_SET_NBIO, 1, NULL); /* create new SSL connection state */ profile_transport[idx].ssl = SSL_new(profile_transport[idx].ctx); SSL_set_connect_state(profile_transport[idx].ssl); /* attach socket */ SSL_set_fd(profile_transport[idx].ssl, profile_transport[index].socket); /* attach the socket descriptor */ /* perform the connection */ if ( SSL_connect(profile_transport[idx].ssl) == -1 ) { ERR_print_errors_fp(stderr); return 1; } showCerts(profile_transport[idx].ssl); return 0; }
static uint8_t *extract_from_mtp(uint8_t *data, size_t *len, int *opc, int *dpc, int *type) { struct mtp_level_3_hdr *hdr; *opc = INT_MAX; *dpc = INT_MAX; if (!data) return NULL; if (*len < sizeof(*hdr)) { LERR("MTP not enough space for mtp hdr %zu vs. %zu", *len, sizeof(*hdr)); return NULL; } hdr = (struct mtp_level_3_hdr *) data; *opc = hdr->opc; *dpc = hdr->dpc; *type = hdr->ser_ind; *len -= sizeof(*hdr); return &hdr->data[0]; }
int clear_ipport(struct ipport_items *ipport ) { if(ipport) { if (pthread_rwlock_wrlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } LDEBUG("DELETING.................."); LDEBUG("NAME: [%s]", ipport->name); HASH_DEL( ipports, ipport); /* free */ free(ipport); pthread_rwlock_unlock(&ipport_lock); return 1; } return 0; }
int load_module(xml_node *config) { char *dev = NULL, *usedev = NULL; char errbuf[PCAP_ERRBUF_SIZE]; xml_node *modules; char *key, *value = NULL; LNOTICE("Loaded proto_rtcp\n"); /* READ CONFIG */ modules = config; while(1) { if(modules == NULL) break; modules = xml_get("param", modules, 1 ); if(modules->attr[0] != NULL && modules->attr[2] != NULL) { /* bad parser */ if(strncmp(modules->attr[2], "value", 5) || strncmp(modules->attr[0], "name", 4)) { LERR( "bad keys in the config\n"); goto next; } key = modules->attr[1]; value = modules->attr[3]; if(key == NULL || value == NULL) { LERR( "bad values in the config\n"); goto next; } if(!strncmp(key, "dev", 3)) usedev = value; else if(!strncmp(key, "portrange", 9)) rtcp_portrange = value; else if(!strncmp(key, "promisc", 7) && !strncmp(value, "false", 5)) rtcp_promisc = 0; else if(!strncmp(key, "filter", 6)) rtcp_userfilter = value; else if(!strncmp(key, "rtcp-json", 9) && !strncmp(value, "false", 5) ) rtcp_as_json = 0; else if(!strncmp(key, "send-sdes", 9) && !strncmp(value, "false", 5) ) send_sdes = 0; else if(!strncmp(key, "vlan", 4) && !strncmp(value, "true", 4)) rtcp_vlan = 1; else if(!strncmp(key, "debug", 5) && !strncmp(value, "true", 4)) debug_proto_rtcp_enable = 1; } next: modules = modules->next; } /* DEV || FILE */ if(!usefile) { dev = usedev ? usedev : pcap_lookupdev(errbuf); if (!dev) { perror(errbuf); exit(-1); } } // start thread pthread_create(&rtp_thread, NULL, rtp_collect, (void *)dev); return 0; }
void* rtp_collect( void* device ) { struct bpf_program filter; char errbuf[PCAP_ERRBUF_SIZE]; char *filter_expr; uint16_t snaplen = 65535, timeout = 100, len = 300, ret = 0; if(device) { if((sniffer_rtp = pcap_open_live((char *)device, snaplen, rtcp_promisc, timeout, errbuf)) == NULL) { LERR("Failed to open packet sniffer on %s: pcap_open_live(): %s\n", (char *)device, errbuf); return NULL; } } else { if((sniffer_rtp = pcap_open_offline(usefile, errbuf)) == NULL) { LERR("Failed to open packet sniffer rtp on %s: pcap_open_offline(): %s\n", usefile, errbuf); return NULL; } } len += (rtcp_portrange != NULL) ? strlen(rtcp_portrange) : 10; len += (rtcp_userfilter != NULL) ? strlen(rtcp_userfilter) : 0; filter_expr = malloc(sizeof(char) * len); ret += snprintf(filter_expr, len, RTCP_FILTER); /* FILTER */ if(rtcp_portrange != NULL) ret += snprintf(filter_expr+ret, (len - ret), "%s portrange %s ", ret ? " and": "", rtcp_portrange); /* CUSTOM FILTER */ if(rtcp_userfilter != NULL) ret += snprintf(filter_expr+ret, (len - ret), " %s", rtcp_userfilter); /* compile filter expression (global constant, see above) */ if (pcap_compile(sniffer_rtp, &filter, filter_expr, 1, 0) == -1) { LERR("Failed to compile filter \"%s\": %s\n", filter_expr, pcap_geterr(sniffer_rtp)); if(filter_expr) free(filter_expr); return NULL; } /* install filter on sniffer session */ if (pcap_setfilter(sniffer_rtp, &filter)) { LERR("Failed to install filter: %s\n", pcap_geterr(sniffer_rtp)); if(filter_expr) free(filter_expr); return NULL; } if(filter_expr) free(filter_expr); /* detect link_offset. Thanks ngrep for this. */ switch(pcap_datalink(sniffer_rtp)) { case DLT_EN10MB: link_offset = ETHHDR_SIZE; break; case DLT_IEEE802: link_offset = TOKENRING_SIZE; break; case DLT_FDDI: link_offset = FDDIHDR_SIZE; break; case DLT_SLIP: link_offset = SLIPHDR_SIZE; break; case DLT_PPP: link_offset = PPPHDR_SIZE; break; case DLT_LOOP: case DLT_NULL: link_offset = LOOPHDR_SIZE; break; case DLT_RAW: link_offset = RAWHDR_SIZE; break; case DLT_LINUX_SLL: link_offset = ISDNHDR_SIZE; break; case DLT_IEEE802_11: link_offset = IEEE80211HDR_SIZE; break; default: LERR( "fatal: unsupported interface type %u\n", pcap_datalink(sniffer_rtp)); exit(-1); } while (pcap_loop(sniffer_rtp, 0, (pcap_handler)rtcpback_proto, 0)); /* terminate from here */ handler(1); return NULL; }