int main (int argc, char **argv) { GMainLoop *loop; GIOChannel *sock; /* Initialize signal handlers */ signal(SIGPIPE, SIG_IGN); signal(SIGINT, handle_sigint); /* read nocat.conf */ read_conf_file( NC_CONF_PATH "/nocat.conf" ); initialize_driver(); /* initialize the firewall */ fw_init( nocat_conf ); /* initialize the peer table */ peer_tab = g_hash_new(); /* initialize the listen socket */ sock = http_bind_socket( CONF("GatewayAddr"), CONFd("GatewayPort"), CONFd("ListenQueue") ); /* initialize the main loop and handlers */ loop = g_main_new(FALSE); g_io_add_watch( sock, G_IO_IN, (GIOFunc) handle_accept, NULL ); g_timeout_add( 30000, (GSourceFunc) check_peers, NULL ); g_timeout_add( 1000, (GSourceFunc) check_sigint, loop ); /* Go! */ g_message("starting main loop"); g_main_run( loop ); g_message("exiting main loop"); return 0; }
peer *peer_new ( GHashTable *conf, const gchar *ip ) { peer *p; gchar *hw; g_assert( ip != NULL ); if (CONFd("Verbosity") >= 5) g_message("peer_new: Creating new peer object for %s.", ip); // Find & Set MAC address. if ( (hw = find_peer_arp( ip )) == NULL ){ if (CONFd("Verbosity") >= 5) g_message( "peer_new: No MAC Address found for peer %s, not allocated!", ip ); return NULL; } p = g_new0( peer, 1 ); g_strncpy(p->hw, hw, strlen(hw)+1 ); // Set IP address. g_strncpy( p->ip, ip, strlen(ip)+1 ); // Set connection time. p->connected = time( NULL ); p->token[0] = '\0'; //peer_extend_timeout(conf, p); p->idle_check =0; p->name = NULL; p->session_id = NULL; p->acct_input_octets=0; p->acct_output_octets=0; p->acct_session_start=0; p->missing_count=0; p->auth_check =0; p->password = NULL; p->expire =0; return p; }
GHashTable *http_parse_header (http_request *h, gchar *req) { GHashTable *head = g_hash_new(); gchar **lines, **items, *key, *val, *p, prefix[50]; guint i; h->method = NULL; h->uri = NULL; h->header = head; if (req == NULL) return head; lines = g_strsplit( req, "\r\n", 0 ); if (lines == NULL || lines[0] == NULL) return head; items = g_strsplit( lines[0], " ", 3 ); h->method = g_strdup( items[0] ); h->uri = g_strdup( items[1] ); // if (CONFd("Verbosity") >= 8) g_message( "http_parse_header: method_len: %d, uri_len: %d", strlen(h->method), strlen(h->uri)); if (CONFd("Verbosity") >= 8) g_message( "http_parse_header: Method: %s", h->method ); if (CONFd("Verbosity") >= 8) g_message( "http_parse_header: URI: %s", url_decode(h->uri) ); g_strfreev( items ); for (i = 1; lines[i] != NULL && lines[i][0] != '\0'; i++ ) { key = lines[i]; val = strchr(key, ':'); if (val != NULL) { /* Separate the key from the value */ *val = '\0'; /* Normalize key -- lowercase every after 1st char */ for (p = key + 1; *p != '\0'; p++) *p = tolower(*p); /* Strip ": " plus leading and trailing space from val */ g_strchomp( val += 2 ); // ": " //if ( strcmp(key, "Referer" )== 0) { // if (CONFd("Verbosity") >= 8) g_message("http_parse_header: Referer: %s", url_decode(val) ); //} //else { if (CONFd("Verbosity") >= 8) { g_snprintf(prefix, 50, "http_parse_header: %s: ", key); syslog_message(prefix, url_decode(val), strlen(url_decode(val)) ); } g_hash_set( head, key, val ); //} } } g_strfreev( lines ); h->header = head; return head; }
gboolean http_request_ok (http_request *h) { guint hdr_len = http_get_hdr_len( h->buffer->str ); guint c_len; if (hdr_len != 0) { if(CONFd("Verbosity") >= 8) g_warning( "http_request_ok: hdr_len %d", hdr_len ); if ( HEADER("Content-length") && (c_len=atoi(HEADER("Content-length")) <= (h->buffer->len - hdr_len)) ) { if(CONFd("Verbosity") >= 8) g_warning( "http_request_ok: Parsing query from HTTP-Content." ); http_parse_query( h, &(h->buffer->str[hdr_len]) ); h->complete++; if(CONFd("Verbosity") >= 8) g_warning( "http_request_ok: Query parsing finished, exiting." ); return TRUE; } else if (HEADER("Content-length") && (CONFd("Verbosity") >= 8)) g_warning( "http_request_ok: HEADER was OK, but CONTENT reading failed." ); else if(CONFd("Verbosity") >= 8) g_warning( "http_request_ok: No Content-length header, parsing query from URI." ); http_parse_query( h, NULL ); if (!h->query) { if (CONFd("Verbosity") >= 8) g_message("http_request_ok: No QUERY found."); } else if (CONFd("Verbosity") >= 8) { GString *z; z = g_hash_as_string( h->query ); g_debug( "http_request_ok: Query: %s", z->str ); g_string_free(z, 1); } h->complete++; if(CONFd("Verbosity") >= 8) g_warning( "http_request_ok: Query parsing finished, exiting." ); return TRUE; } if(CONFd("Verbosity") >= 6) g_warning( "http_request_ok: Request not HTTP: <CR><LF><CR><LF> (header_end) NOT found" ); return FALSE; }
int main (int argc, char **argv) { GMainLoop *loop; GIOChannel *sock; /* read nocat.conf */ read_conf_file( NC_CONF_PATH "/nocat.conf" ); if (argc < 2 || strncmp(argv[1], "-D", 2) != 0) daemonize(); /* initalize the log */ initialize_log(); /* set network parameters */ set_network_defaults( nocat_conf ); /* initialize the gateway type driver */ initialize_driver(); /* initialize the firewall */ fw_init( nocat_conf ); /* initialize the peer table */ peer_tab = g_hash_new(); /* initialize the listen socket */ sock = http_bind_socket( CONF("GatewayAddr"), CONFd("GatewayPort"), CONFd("ListenQueue") ); /* initialize the main loop and handlers */ loop = g_main_new(FALSE); g_io_add_watch( sock, G_IO_IN, (GIOFunc) handle_accept, NULL ); g_timeout_add( 30000, (GSourceFunc) check_peers, NULL ); g_timeout_add( 1000, (GSourceFunc) check_exit_signal, loop ); /* Go! */ g_message("starting main loop"); g_main_run( loop ); g_message("exiting main loop"); return 0; }
GHashTable *parse_query_string( gchar *query ) { GHashTable *data = g_hash_new(); gchar **items, *key, *val, prefix[50]; guint i; if (!query) return data; items = g_strsplit( query, "&", 0 ); for ( i = 0; items[i] != NULL; i++ ) { key = items[i]; if (key == NULL) break; val = strchr( key, '=' ); if (val != NULL) *(val++) = '\0'; else val = "1"; key = url_decode( key ); val = url_decode( val ); /* Irving - fix from Yurgi - check to see if the key is already in the hash table. This deals with keys that are set twice by web sites */ if(g_hash_table_lookup_extended( data, key, NULL, NULL ) == FALSE ) { g_hash_set( data, key, val ); if(CONFd("Verbosity") >= 8) { g_snprintf(prefix, 50, "parse_query_string: %s=", key); syslog_message(prefix, val, strlen(val)); } } else if(CONFd("Verbosity") >= 8) g_message("parse_query_string: DUPLICATE key %s=%s, discarded.", key, val); g_free( key ); g_free( val ); } g_strfreev(items); return data; }
GHashTable *http_parse_query (http_request *h, gchar *post) { gchar *q = NULL; g_assert( h != NULL ); if (h->uri != NULL) { if(CONFd("Verbosity") >= 9) g_message( "http_parse_query: URI: %s", url_decode(h->uri) ); q = strchr( h->uri, '?' ); } if (post != NULL) { h->query = parse_query_string( post ); } else if (q != NULL) { h->query = parse_query_string( q + 1 ); } else { h->query = NULL; } if (q != NULL) *q = '\0'; /* remove the query string from the URI */ return h->query; }
static int fw_exec( fw_action *act, GHashTable *conf ) { GHashTable *data; GPtrArray *env; gchar *cmd, **arg, **n; data = g_hash_dup( conf ); // // Than add specifics about this particular client, if any if (act->p != NULL) { g_hash_set( data, "IP", act->p->ip ); g_hash_set( data, "MAC", act->p->hw ); g_hash_set( data, "Class", "Public" ); } cmd = conf_string( conf, act->cmd ); cmd = parse_template( cmd, data ); if (CONFd("Verbosity") >= 5) g_message("Got command %s from action %s", cmd, act->cmd ); arg = g_strsplit( cmd, " ", 0 ); // prime the environment with our existing environment env = g_ptr_array_new(); for ( n = environ; *n != NULL; n++ ) g_ptr_array_add( env, *n ); // Then add everything from the conf file g_hash_table_foreach( data, (GHFunc) fw_exec_add_env, env ); // Add a closing NULL so execve knows where to lay off. g_ptr_array_add( env, NULL ); /* We're not cleaning up memory references because * hopefully the exec won't fail... */ execve( *arg, arg, (char **)env->pdata ); g_error( "execve %s failed: %m", cmd ); // Shouldn't happen. return -1; }
guint http_request_read (http_request *h) { gchar *buf = g_new( gchar, BUFSIZ + 1 ); /* GIOStatus r; GError *err = NULL; */ GIOError r; guint s, times, n = 0, t = 0; gchar *c_len_hdr = NULL; guint hdr_len = 0, c_len = 0, tot_req_size = 0; struct timeval tv; fd_set fdset; FD_ZERO(&fdset); FD_SET(g_io_channel_unix_get_fd(h->sock), &fdset); tv.tv_sec = 0; buf[0] = '\0'; buf[BUFSIZ] = '\0'; // for (t = 0, n = BUF_SIZ; n == BUF_SIZ && // h->buffer->len < MAX_REQUEST_SIZE; t += n ) { // BPsmythe: The above (original) loop will never execute // more than once unless the size of the buffer read in (n) // is equal to the constant BUF_SIZE. What is desired is // to keep looping until there is nothing left to read. // The for was changed to look for the end of the headers. // FIXME: We should use the newer g_io_channel_read_char // // TJaqua: Added buffer overflow checking, content read loop from 0.93pre2, and fixed up the timeouts, logging and error exits if (CONFd("Verbosity") >= 7) g_message("http_request_read: READING request from peer %s (on %s, fd: %d)", h->peer_ip, h->sock_ip, g_io_channel_unix_get_fd(h->sock)); if (CONFd("Verbosity") >= 9) g_message("http_request_read: entering HEADER read loop (BUFSIZE=%u)", BUFSIZ); for (times=MAX_REQ_TIMEOUTS; !hdr_len && (times > 0); t += n ) { n=0; /* r = g_io_channel_read_chars( h->sock, buf, BUFSIZ, &n, &err ); if (r == G_IO_STATUS_ERROR || err != NULL) { g_message( "http_request_read: Socket IO ERROR: %s, exiting!", err->message ); g_error_free(err); */ r = g_io_channel_read( h->sock, buf, BUFSIZ, &n); if (r != G_IO_ERROR_NONE) { g_warning( "http_request_read: Socket IO ERROR: %m, exiting!" ); g_free(buf); return 0; } if (CONFd("Verbosity") >= 9) g_message("http_request_read: HEADER loop read %u bytes", n); buf[n] = '\0'; if (n && CONFd("Verbosity") >= 11) syslog_message("RAW_HDR_BUF: ", buf, n); if(strlen(buf) < n-1) g_warning("http_request_read: Trailing data past string in buffer was DICARDED!"); if (h->buffer->len == MAX_REQUEST_SIZE) g_warning("http_request_read: header buffer full (%u bytes, %u bytes discarded!)", MAX_REQUEST_SIZE, n); else if (n <= (MAX_REQUEST_SIZE - h->buffer->len)) { if(CONFd("Verbosity") >= 11) g_message("http_request_read: APPENDING buffer to HEADER."); if(n) g_string_append(h->buffer, buf); else if (CONFd("Verbosity") >= 6) g_message("http_request_read: No data in buffer."); //if(CONFd("Verbosity") >= 11) g_message("http_request_read: Partial HEADER (%u bytes) is: %s", h->buffer->len, h->buffer->str); } else { if(CONFd("Verbosity") >= 6) g_warning("http_request_read: header buffer full (%u bytes, %u bytes discarded!)", MAX_REQUEST_SIZE, n - (MAX_REQUEST_SIZE - h->buffer->len)); buf[MAX_REQUEST_SIZE - h->buffer->len] = '\0'; g_string_append(h->buffer, buf); } times--; // BPsmythe: Check for the end of the headers. if (hdr_len = http_get_hdr_len(h->buffer->str)) { if (CONFd("Verbosity") >= 6) g_message("http_request_read: HEADER END found, length: %u", hdr_len ); } else { if(!times) { if(CONFd("Verbosity") >= 6) { g_message("http_request_read: ERROR: HEADER END not found after %d tries, exiting!", MAX_REQ_TIMEOUTS); if (!t) g_message("http_request_read: Empty HTTP-request header."); else g_message("http_request_read: Invalid HTTP-request header, %u bytes read.", t+n); } //Should I send a FIN packet to shutdown the socket? g_free(buf); return 0; } if(CONFd("Verbosity") >= 11) g_message("http_request_read: Waiting for next I/O on socket, (%d more tries).", times); tv.tv_usec = REQ_TIMEOUT; while(times && !(s = select (g_io_channel_unix_get_fd(h->sock)+1, &fdset, NULL, NULL, &tv))) { times--; if (CONFd("Verbosity") >= 7) g_message("http_request_read: HEADER select timeout, %d more tries", times); tv.tv_usec = REQ_TIMEOUT; } if(s<0) { g_warning("http_request_read: ERROR in select, exiting!"); g_free(buf); return 0; } else if(!times) { if(CONFd("Verbosity") >= 6) { g_message("http_request_read: ERROR: Too many TIMEOUTS waiting for HEADER end!"); if (!t) g_message("http_request_read: Empty HTTP-request header."); else g_message("http_request_read: Invalid HTTP-request header, %u bytes read.", t+n); } //Should I send a FIN packet to shutdown the socket? g_free(buf); return 0; } else if(CONFd("Verbosity") >= 11) g_message("http_request_read: Recieved I/O on socket."); } } if (CONFd("Verbosity") >= 10) syslog_message("RAW_HEADER: ", h->buffer->str, h->buffer->len); // Read the content length from the header http_parse_header( h, h->buffer->str ); if( (HEADER("Content-length")) && (c_len = atoi(HEADER("Content-length"))) ) { if (CONFd("Verbosity") >= 9) g_message("http_request_read: entering CONTENT read loop to read %u bytes.", c_len); tot_req_size = hdr_len + c_len; for (times=MAX_REQ_TIMEOUTS; (t < tot_req_size) && (times > 0); t += n ) { if (CONFd("Verbosity") >= 9) g_message("http_request_read: %u bytes of %u total.", t, tot_req_size ); n=0; /* r = g_io_channel_read_chars( h->sock, buf, BUFSIZ, &n, &err ); if (r == G_IO_STATUS_ERROR || err != NULL) { g_message( "http_request_read: Socket-IO ERROR: %s, exiting!", err->message ); g_error_free(err); */ r = g_io_channel_read( h->sock, buf, BUFSIZ, &n); if (r != G_IO_ERROR_NONE) { g_warning( "http_request_read: Socket-IO ERROR: %m, exiting!" ); g_free(buf); return 0; } if (CONFd("Verbosity") >= 9) g_message("http_request_read: CONTENT loop read %d bytes", n ); buf[n] = '\0'; if (n && CONFd("Verbosity") >= 11) syslog_message("RAW_CON_BUF: ", buf, n); if (h->buffer->len == MAX_REQUEST_SIZE) { if(CONFd("Verbosity") >= 6) g_warning("http_request_read: Maximum request length EXCEEDED (%u bytes discarded! Continuing to read out content.)", n); } else if (h->buffer->len == tot_req_size) { if(CONFd("Verbosity") >= 6) g_warning("http_request_read: Content length EXCEEDED (%u bytes discarded! Shouldn't happen!)", n); } else if (h->buffer->len + n >= MAX_REQUEST_SIZE) { if(CONFd("Verbosity") >= 6) g_warning("http_request_read: Max buffer length EXCEEDED (%u bytes discarded! Continuing to read out content.)", n - (MAX_REQUEST_SIZE - h->buffer->len)); buf[MAX_REQUEST_SIZE - h->buffer->len] = '\0'; g_string_append(h->buffer, buf); } else if (n <= (tot_req_size - h->buffer->len)) { if(CONFd("Verbosity") >= 11) g_message("http_request_read: APPENDING buffer to CONTENT."); if(n) g_string_append(h->buffer, buf); else if (CONFd("Verbosity") >= 6) g_message("http_request_read: No data in buffer."); } else { if(CONFd("Verbosity") >= 6) g_warning("http_request_read: Content length EXCEEDED (%u bytes added, %u bytes discarded!)", tot_req_size - h->buffer->len, n - (tot_req_size - h->buffer->len)); buf[tot_req_size - h->buffer->len] = '\0'; g_string_append(h->buffer, buf); } times--; // TJaqua: Check for the end of the content. if ((t+n) >= tot_req_size) { if (CONFd("Verbosity") >= 6) g_message("http_request_read: CONTENT end reached, length: %u", tot_req_size); } else { if (!times) { if(CONFd("Verbosity") >= 6) { g_message("http_request_read: ERROR: CONTENT END not found after %d tries, exiting!", MAX_REQ_TIMEOUTS); if (t == hdr_len) g_message("http_request_read: Empty CONTENT, socket may have stalled."); else g_message("http_request_read: CONTENT unfinished, %u bytes read.", t+n); } //Should I send a FIN packet to shutdown the socket? g_free(buf); //We still got the header, though, so return it. g_string_truncate(h->buffer, hdr_len); return hdr_len; } if(CONFd("Verbosity") >= 11) g_message("http_request_read: Waiting for next I/O on socket."); tv.tv_usec = REQ_TIMEOUT; while (times && !(s = select (g_io_channel_unix_get_fd(h->sock)+1, &fdset, NULL, NULL, &tv))) { times--; if (CONFd("Verbosity") >= 7) g_message("http_request_read: CONTENT select timeout, %d more tries", times); tv.tv_usec = REQ_TIMEOUT; } if(s<0) { g_warning("http_request_read: ERROR in select, exiting!"); g_free(buf); //We still got the header, though, so return it. g_string_truncate(h->buffer, hdr_len); return hdr_len; } else if (!times) { if(CONFd("Verbosity") >= 6) { g_message("http_request_read: ERROR: Too many TIMEOUTS waiting for CONTENT end!"); if (t == hdr_len) g_message("http_request_read: Empty CONTENT, socket may have stalled."); else g_message("http_request_read: Invalid HTTP-request header, %u bytes read.", t+n); } //Should I send a FIN packet to shutdown the socket? g_free(buf); //We still got the header, though, so return it. g_string_truncate(h->buffer, hdr_len); return hdr_len; } else if (CONFd("Verbosity") >= 11) g_message("http_request_read: Received I/O on socket."); } } if (CONFd("Verbosity") >= 10) syslog_message("RAW_CONTENT: ", &(h->buffer->str[hdr_len]), h->buffer->len - hdr_len); if (t<tot_req_size) g_message("http_request_read: CONTENT unfinished - should not happen!"); } if (CONFd("Verbosity") >= 6) g_message("http_request_read: FINISHED read (%u bytes total), exiting.", t); g_free(buf); return t; }