Exemplo n.º 1
0
/**
 * This function defines the request type with the hostname and port based on the URI gathered
 * from the buffer.
 *
 * This is for example used for
 * CONNECT IP:PORT HTTP/1.0
 * types of requests.
 *
 * If :PORT is not found, then req.http_infos.port is left untouched (must be defined priorly).
 *
 * @return 0 on success, -1 if error
 */
static int get_hostname_from_uri(request_t* req, int offset)
{
        char *ptr, *buf;

        buf = req->data + offset;

        /* isolate the whole block 'IP:PORT' */
        ptr = strchr(buf, ' ');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Invalid URI block");
                return -1;
        }

        *ptr = '\0';
        buf = proxenet_xstrdup2(buf);

        /* host and port */
        ptr = strchr(buf, ':');
        if (ptr){
                /* explicit port */
                req->http_infos.port = (unsigned short)atoi(ptr+1);
                *ptr = '\0';
        }

        req->http_infos.hostname = proxenet_xstrdup2(buf);

        proxenet_xfree(buf);
        return 0;
}
Exemplo n.º 2
0
/**
 * Look up for a header in a request. If found, this function will allocate a buffer containing
 * the header value (after the ': ') until a line RET (CR/LF) is found.
 * If not found, it returns NULL.
 *
 * @note if found, the allocated buffer *MUST* be free-ed by caller.
 * @return a pointer to a malloc-ed buffer with the header value, or NULL if not found.
 */
static char* get_header_by_name(char* request, const char* header_name)
{
        char *ptr, *ptr2, c, *header_value;

        /* get header */
        ptr = strstr(request, header_name);
        if(!ptr){
                return NULL;
        }

        /* move to start of hostname  */
        ptr += strlen(header_name);
        ptr2 = ptr;

        /* get the end of header line */
        for(; *ptr2 && *ptr2!='\r' && *ptr2!='\n'; ptr2++);
        c = *ptr2;
        *ptr2 = '\0';

        /* copy the value  */
        header_value = proxenet_xstrdup2(ptr);

        *ptr2 = c;
        proxenet_strip(header_value);
        return header_value;
}
Exemplo n.º 3
0
/**
 * Look up for a header in a request. If found, this function will allocate a buffer containing
 * the header value (after the ': ') until a line RET (CR/LF) is found.
 * If not found, it returns NULL.
 *
 * @note if found, the allocated buffer *MUST* be free-ed by caller.
 * @return a pointer to a malloc-ed buffer with the header value, or NULL if not found.
 */
static char* get_header_by_name(char* request, const char* header_name)
{
        char *ptr, *ptr2, c, *header_value;

        /* get header */
        ptr = strstr(request, header_name);
        if(!ptr){
                xlog(LOG_ERROR, "Header '%s' not found\n", header_name);
                return NULL;
        }

        /* move to start of hostname  */
        ptr += strlen(header_name);
        ptr2 = ptr;

        /* get the end of header line */
        for(; *ptr2 && *ptr2!='\r' && *ptr2!='\n'; ptr2++);
        c = *ptr2;
        *ptr2 = '\0';

        /* copy the value  */
        header_value = proxenet_xstrdup2(ptr);
        if (!header_value){
                xlog(LOG_ERROR, "strdup(header '%s') failed.\n", header_name);
                return NULL;
        }

        *ptr2 = c;
        return header_value;
}
Exemplo n.º 4
0
static int get_hostname_from_header(request_t *req)
{
        char *ptr, *header;

        header = get_header_by_name(req->data, "Host:");
        if (!header){
                return -1;
        }

        /* if port number, copy it */
        ptr = strchr(header, ':');
        if (ptr){
                *ptr = '\0';
                req->http_infos.hostname = proxenet_xstrdup2(header);
                req->http_infos.port = (unsigned short)atoi(ptr+1);
        } else {
                req->http_infos.hostname = proxenet_xstrdup2(header);
        }

        proxenet_xfree(header);

        return 0;
}
Exemplo n.º 5
0
/**
 * Check that an argument is a valid file path by
 * - solving the path
 * - ensuring result is read accessible
 *
 * @param param is the parameter to validate
 * @return the real (no symlink) full path of the file it is valid
 * @return NULL in any other case
 */
static char* cfg_get_valid_file(char* param)
{
        char buf[PATH_MAX+1]={0, };

        if (!realpath(param, buf)){
                xlog(LOG_CRITICAL, "realpath(%s) failed: %s\n", param, strerror(errno));
                return NULL;
        }

        if ( !is_readable_file(buf) ){
                xlog(LOG_CRITICAL, "Failed to read private key '%s'\n", param);
                return NULL;
        }

        return proxenet_xstrdup2(buf);
}
Exemplo n.º 6
0
/**
 * This function updates all the fields of the current request_t with the new values found in the
 * request. Since those values will be useful many times, they are strdup-ed in a specific structure
 * (http_infos_t). Those values *must* be free-ed later on.
 *
 * @return 0 if successful, -1 if any error occurs.
 */
int parse_http_request(request_t *req)
{
        char *ptr, *buf, c;
        int offset;

        buf = req->data;

        /* method */
        ptr = strchr(buf, ' ');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP method in request");
                if (cfg->verbose)
                        xlog(LOG_ERROR, "Buffer sent:\n%s\n", buf);
                return -1;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.method = proxenet_xstrdup2(buf);
        if (!req->http_infos.method){
                xlog(LOG_ERROR, "%s\n", "strdup(method) failed, cannot pursue...");
                return -1;
        }

        *ptr = c;

        req->http_infos.proto_type = HTTP;
        if (!strcmp(req->http_infos.method, "CONNECT")){
                /*
                 * We can receive a CONNECT if
                 * - the connection uses HTTPS
                 * - the client tries to upgrade to WebSocket/Secure WebSocket
                 */
                char *upgrade_header = get_header_by_name(buf, "Upgrade:");
                if (upgrade_header){
                        if (strcmp(upgrade_header, "WebSocket")==0){
                                xlog(LOG_INFO, "%s\n", "Upgrading to WebSocket");
                                req->http_infos.proto_type = WS;
                                req->is_ssl = false;
                                req->http_infos.proto = WS_STRING;
                                req->http_infos.port = HTTP_DEFAULT_PORT;
                                req->http_infos.proto_type = WS;
                        }
                        proxenet_xfree(upgrade_header);
                } else {
                        req->is_ssl = true;
                        req->http_infos.proto = HTTPS_STRING;
                        req->http_infos.port = HTTPS_DEFAULT_PORT;
                        req->http_infos.proto_type = HTTPS;
                }

                offset = ptr - buf + 1;

                if( get_hostname_from_uri(req, offset) < 0 ){
                        xlog(LOG_ERROR, "%s\n", "Failed to get hostname (URI)");
                        goto failed_hostname;
                }

                req->http_infos.path = proxenet_xstrdup2("/");
                req->http_infos.version = proxenet_xstrdup2("HTTP/1.0");

                req->http_infos.uri = get_request_full_uri(req);
                if(!req->http_infos.uri){
                        xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed");
                        goto failed_uri;
                }

                return 0;
        }


        /* hostname and port */
        if (req->is_ssl){
                req->http_infos.port = HTTPS_DEFAULT_PORT;
                req->http_infos.proto = HTTPS_STRING;
        } else {
                req->http_infos.port = HTTP_DEFAULT_PORT;
                req->http_infos.proto = HTTP_STRING;
        }

        if( get_hostname_from_header(req) < 0 ){
                xlog(LOG_ERROR, "%s\n", "Failed to get hostname (Host header)");
                goto failed_hostname;
        }


        /* path */
        buf = ptr+1;

        if (!strncmp(buf, HTTP_PROTO_STRING, strlen(HTTP_PROTO_STRING))){
                buf = strchr(buf + 8, '/');
        }

        ptr = strchr(buf, ' ');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP path in request");
                goto failed_path;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.path = proxenet_xstrdup2(buf);
        if (!req->http_infos.path){
                xlog(LOG_ERROR, "%s\n", "strdup(path) failed, cannot pursue...");
                goto failed_path;
        }
        *ptr = c;

        buf = ptr+1;


        /* version */
        ptr = strchr(req->data, '\r');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP version");
                goto failed_version;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.version = proxenet_xstrdup2(buf);
        if (!req->http_infos.version){
                xlog(LOG_ERROR, "%s\n", "strdup(version) failed, cannot pursue...");
                goto failed_version;
        }
        *ptr = c;


        /* refresh uri */
        req->http_infos.uri = get_request_full_uri(req);
        if(!req->http_infos.uri){
                xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed");
                goto failed_uri;
        }


        if (cfg->verbose) {
                xlog(LOG_INFO, "New request %d to '%s'\n",
                     req->id, req->http_infos.uri);

                if (cfg->verbose > 1) {
                        xlog(LOG_INFO,
                             "Request HTTP information:\n"
                             "method=%s\n"
                             "proto=%s\n"
                             "hostname=%s\n"
                             "port=%d\n"
                             "path=%s\n"
                             "version=%s\n"
                             ,
                             req->http_infos.method,
                             req->http_infos.proto,
                             req->http_infos.hostname,
                             req->http_infos.port,
                             req->http_infos.path,
                             req->http_infos.version);
                }
        }

        return 0;


failed_uri:
        proxenet_xfree(req->http_infos.version);

failed_version:
        proxenet_xfree(req->http_infos.path);

failed_path:
        proxenet_xfree(req->http_infos.hostname);

failed_hostname:
        proxenet_xfree(req->http_infos.method);

        return -1;
}
Exemplo n.º 7
0
/**
 * Checks command line arguments once and for all. Handles (de)allocation for config_t structure
 *
 * @return 0 if successfully parsed and allocated
 * @return -1
 */
static int parse_options (int argc, char** argv)
{
        int curopt, curopt_idx;
        char *logfile, *plugin_path;
        char *keyfile, *keyfile_pwd, *certfile;
        char *sslcli_keyfile, *sslcli_keyfile_pwd, *sslcli_certfile, *sslcli_domain;
        char *proxy_host, *proxy_port;
        char *intercept_pattern;
        bool use_socks_proxy;

        proxy_host = proxy_port = NULL;
        use_socks_proxy = false;

        const struct option long_opts[]  = {
                { "version",                           0, 0, 'V' },
                { "help",                              0, 0, 'h' },
                { "verbose",                           0, 0, 'v' },
                { "daemon",                            0, 0, 'd' },
                { "no-color",                          0, 0, 'n' },
                { "plugins",                           1, 0, 'x' },
                { "logfile",                           1, 0, 'l' },

                { "intercept-only",                    0, 0, 'E' },
                { "intercept-except",                  0, 0, 'I' },
                { "match",                             1, 0, 'm' },

                { "nb-threads",                        1, 0, 't' },
                { "bind",                              1, 0, 'b' },
                { "port",                              1, 0, 'p' },
                { "proxy-host",                        1, 0, 'X' },
                { "proxy-port",                        1, 0, 'P' },
                { "use-socks",                         0, 0, 'D' },

                { "certfile",                          1, 0, 'c' },
                { "keyfile",                           1, 0, 'k' },
                { "keyfile-passphrase",                1, 0, 'K' },

                { "sslcli-certfile",                   1, 0, 'z' },
                { "sslcli-domain",                     1, 0, 'Z' },
                { "sslcli-keyfile",                    1, 0, 'y' },
                { "sslcli-keyfile-passphrase",         1, 0, 'Y' },

                { "ie-compat",                         0, 0, 'i'},
                { "no-ssl-intercept",                  0, 0, 'N'},

                { 0, 0, 0, 0 }
        };

        cfg->iface		= CFG_DEFAULT_BIND_ADDR;
        cfg->port		= CFG_DEFAULT_BIND_PORT;
        cfg->logfile_fd		= CFG_DEFAULT_OUTPUT;
        cfg->nb_threads		= CFG_DEFAULT_NB_THREAD;
        cfg->use_color		= true;
        cfg->ip_version		= CFG_DEFAULT_IP_VERSION;
        cfg->try_exit		= 0;
        cfg->try_exit_max	= CFG_DEFAULT_TRY_EXIT_MAX;
        cfg->daemon		= false;

        cfg->intercept_mode	= INTERCEPT_ONLY;
        intercept_pattern	= CFG_DEFAULT_INTERCEPT_PATTERN;

        plugin_path		= CFG_DEFAULT_PLUGINS_PATH;
        logfile			= NULL;

        certfile		= CFG_DEFAULT_SSL_CERTFILE;
        keyfile			= CFG_DEFAULT_SSL_KEYFILE;
        keyfile_pwd		= CFG_DEFAULT_SSL_KEYFILE_PWD;

        cfg->certsdir		= CFG_DEFAULT_SSL_CERTSDIR;
        cfg->certskey		= CFG_DEFAULT_SSL_CERTSKEY;
        cfg->certskey_pwd	= CFG_DEFAULT_SSL_CERTSPWD;

        sslcli_certfile		= NULL;
        sslcli_domain		= CFG_DEFAULT_SSL_CLIENT_DOMAIN;
        sslcli_keyfile		= NULL;
        sslcli_keyfile_pwd	= CFG_DEFAULT_SSL_KEYFILE_PWD;

        cfg->ie_compat          = false;
        cfg->ssl_intercept      = true;


        /* parse command line arguments */
        while (1) {
                curopt_idx = 0;
                curopt = getopt_long (argc,argv,
                                      "dn46Vhvb:p:l:t:c:k:K:x:X:P:z:y:Y:IEm:NiD",
                                      long_opts, &curopt_idx);
                if (curopt == -1) break;

                switch (curopt) {
                        case 'v': cfg->verbose++; break;
                        case 'b': cfg->iface = optarg; break;
                        case 'p': cfg->port = optarg; break;
                        case 'l': logfile = optarg; break;
                        case 't': cfg->nb_threads = (unsigned short)atoi(optarg); break;
                        case 'X':
                                proxy_host = optarg;
                                proxy_port = CFG_DEFAULT_PROXY_PORT;
                                break;
                        case 'P': proxy_port = optarg; break;
                        case 'D': use_socks_proxy = true; break;
                        case 'c': certfile = optarg; break;
                        case 'k': keyfile = optarg; break;
                        case 'K': keyfile_pwd = optarg; break;
                        case 'h': help(); break;
                        case 'V': version(true); break;
                        case 'n': cfg->use_color = false; break;
                        case '4': cfg->ip_version = AF_INET; break;
                        case '6': cfg->ip_version = AF_INET6; break;
                        case 'x': plugin_path = optarg; break;
                        case 'd': cfg->daemon = true; break;
                        case 'I': cfg->intercept_mode = INTERCEPT_ONLY; break;
                        case 'E': cfg->intercept_mode = INTERCEPT_EXCEPT; break;
                        case 'm': intercept_pattern = optarg; break;
                        case 'z': sslcli_certfile = optarg; break;
                        case 'Z': sslcli_domain = optarg; break;
                        case 'y': sslcli_keyfile = optarg; break;
                        case 'Y': sslcli_keyfile_pwd = optarg; break;
                        case 'N': cfg->ssl_intercept = false; break;
                        case 'i':
                                cfg->ie_compat = true;
                                xlog(LOG_WARNING, "%s\n", "Enabling IE compatibility mode.");
                                xlog(LOG_WARNING, "%s\n", "This mode should not be used with anything but IE <10.");
                                break;

                        case '?':
                        default:
                                usage (EXIT_FAILURE);
                }
                curopt_idx = 0;
        }

        /* validate command line arguments */

        /* logfile validation : if a logfile is given, use its FILE* for output */
        if (logfile) {
                cfg->logfile = realpath(logfile, NULL);
                if (cfg->logfile == NULL){
                        xlog(LOG_CRITICAL, "realpath(logfile) failed: %s\n", strerror(errno));
                        return -1;
                }
                if(is_readable_file(cfg->logfile)) {
                        cfg->logfile_fd = fopen(cfg->logfile, "w");
                        if(!cfg->logfile_fd) {
                                cfg->logfile_fd = stderr;
                                xlog(LOG_CRITICAL, "[-] Failed to open '%s': %s\n", cfg->logfile, strerror(errno));
                                return -1;
                        }
                }
        }

        /* check if nb of threads is in boundaries */
        if (cfg->nb_threads > MAX_THREADS) {
                fprintf(stderr, "Thread number invalid. Setting to default.\n");
                cfg->nb_threads = CFG_DEFAULT_NB_THREAD;
        }

        /* setting the interception mode */
        cfg->intercept_pattern = proxenet_xstrdup2(intercept_pattern);
        if(!cfg->intercept_pattern)
                return -1;

        if(cfg->verbose) {
                switch(cfg->intercept_mode){
                        case INTERCEPT_ONLY:
                                xlog(LOG_INFO, "Intercepting only matching '%s'\n", cfg->intercept_pattern);
                                break;
                        case INTERCEPT_EXCEPT:
                                xlog(LOG_INFO, "Intercepting everything except matching '%s'\n", cfg->intercept_pattern);
                                break;
                }
        }


        /* check plugins path */
        if (!is_valid_plugin_path(plugin_path, &cfg->plugins_path, &cfg->autoload_path))
                return -1;

#ifdef DEBUG
        xlog(LOG_DEBUG, "Valid plugin tree for '%s' and '%s'\n", cfg->plugins_path, cfg->autoload_path);
#endif

        /* validate proxenet SSL configuration for interception */
        /* check ssl certificate */
        cfg->cafile = cfg_get_valid_file(certfile);
        if (!cfg->cafile)
                return -1;
        /* check ssl key */
        cfg->keyfile = cfg_get_valid_file(keyfile);
        if (!cfg->cafile)
                return -1;
        /* get the key passphrase */
        cfg->keyfile_pwd = proxenet_xstrdup2(keyfile_pwd);
        if (!cfg->keyfile_pwd)
                return -1;

        /* validate proxenet client certificate paramaters */
        /* check ssl client certificate if provided */
        if (sslcli_certfile && sslcli_keyfile) {
                cfg->sslcli_certfile = cfg_get_valid_file(sslcli_certfile);
                if (!cfg->sslcli_certfile)
                        return -1;
                /* check ssl private key associated with the client certificate */
                cfg->sslcli_keyfile = cfg_get_valid_file(sslcli_keyfile);
                if (!cfg->sslcli_keyfile)
                        return -1;
                /* get the key passphrase */
                cfg->sslcli_keyfile_pwd = proxenet_xstrdup2(sslcli_keyfile_pwd);
                if (!cfg->sslcli_keyfile_pwd)
                        return -1;
                /* get the domain */
                cfg->sslcli_domain = proxenet_xstrdup2(sslcli_domain);
                if(!cfg->sslcli_domain)
                        return -1;
        }

        /* check proxy values (if any) */
        if ( proxy_port && !proxy_host) {
                xlog(LOG_CRITICAL, "%s\n", "Cannot use proxy-port without proxy-host");
                return -1;
        }

        if (proxy_host) {
                cfg->proxy.host = proxenet_xstrdup2(proxy_host);
                if (!cfg->proxy.host) {
                        xlog(LOG_CRITICAL, "proxy %s\n", strerror(errno));
                        return -1;
                }

                cfg->proxy.port = proxenet_xstrdup2(proxy_port);
                if (!cfg->proxy.port) {
                        xlog(LOG_CRITICAL, "proxy_port %s\n", strerror(errno));
                        return -1;
                }

                if (use_socks_proxy)
                        cfg->is_socks_proxy = true;
        }

        /* become a daemon */
        if(cfg->daemon) {
                if (cfg->verbose){
                        xlog(LOG_WARNING, "%s will now run as daemon\n", PROGNAME);
                        xlog(LOG_WARNING, "%s\n", "Use `control-client.py' to interact with the process.");
                }

                if (daemon(0, 0) < 0) {
                        xlog(LOG_ERROR, "daemon failed: %s\n", strerror(errno));
                        return -1;
                }

                cfg->verbose = 0;
        }

        return 0;

}
Exemplo n.º 8
0
/**
 * This function is called by all threads to treat to process the request and response.
 * It will also apply the plugins.
 *
 * @param server_socket is the socket received by the main thread from the web browser (acting like server
 * to web browser)
 */
void proxenet_process_http_request(sock_t server_socket)
{
        sock_t client_socket;
        request_t req;
        int retcode, n;
        fd_set rfds;
        struct timespec ts;
        ssl_context_t ssl_context;
        bool is_ssl;
        sigset_t emptyset;
        bool is_new_http_connection = false;

        client_socket = retcode = n = -1;
        proxenet_xzero(&req, sizeof(request_t));
        proxenet_xzero(&ssl_context, sizeof(ssl_context_t));

        /* wait for any event on sockets */
        while(proxy_state == ACTIVE) {

                if (server_socket < 0) {
                        xlog(LOG_ERROR, "sock browser->%s (#%d) died unexpectedly\n", PROGNAME, server_socket);
                        break;
                }

                ts.tv_sec  = HTTP_TIMEOUT_SOCK;
                ts.tv_nsec = 0;

                FD_ZERO(&rfds);
                FD_SET(server_socket, &rfds);
                if (client_socket > 0)
                        FD_SET(client_socket, &rfds);

                sigemptyset(&emptyset);
                retcode = pselect(FD_SETSIZE, &rfds, NULL, NULL, &ts, &emptyset);
                if (retcode < 0) {
                        if (errno == EINTR) {
                                continue;
                        } else {
                                xlog(LOG_CRITICAL, "[thread] pselect returned %d: %s\n", retcode, strerror(errno));
                                break;
                        }
                }

                if (retcode == 0) {
                        continue;
                }

                is_ssl = ssl_context.use_ssl;

                /* is there data from web browser to proxy ? */
                if( FD_ISSET(server_socket, &rfds ) ) {

                        if(is_ssl && req.do_intercept) {
                                n = proxenet_read_all(server_socket,
                                                      &req.data,
                                                      &(ssl_context.server.context));
                        } else {
                                n = proxenet_read_all(server_socket, &req.data, NULL);
                        }

#ifdef DEBUG
                        xlog(LOG_DEBUG, "Got %dB from client (%s srv_sock=#%d intercept_flag=%s)\n",
                             n, is_ssl?"SSL":"PLAIN", server_socket,
                             req.do_intercept?"true":"false");
#endif

                        if (n < 0) {
                                xlog(LOG_ERROR, "%s\n", "read() failed, end thread");
                                break;
                        }

                        if (n == 0){
#ifdef DEBUG
                                xlog(LOG_DEBUG, "%s\n", "Socket EOF from client");
#endif
                                break;
                        }


                        /* from here, n can only be positive */
                        req.size = (size_t) n;
                        bytes_sent += n;

                        if (req.id > 0 && !req.do_intercept){
#ifdef DEBUG
                                xlog(LOG_DEBUG, "Intercept disabled for browser->'%s'\n", req.http_infos.hostname);
#endif
                                goto send_to_server;
                        }

                        /* proxy keep-alive */
                        if (req.id > 0){
                                request_t* old_req = (request_t*)proxenet_xmalloc(sizeof(request_t));
                                memcpy(old_req, &req, sizeof(request_t));
                                char* host = proxenet_xstrdup2( req.http_infos.hostname );

                                free_http_infos(&(req.http_infos));

                                if (update_http_infos(&req) < 0){
                                        xlog(LOG_ERROR, "Failed to update HTTP information for request %d\n", req.id);
                                        proxenet_xfree( host );
                                        proxenet_xfree( old_req );
                                        req.id = 0;
                                        break;
                                }

                                if (strcmp( host, req.http_infos.hostname )){
                                        /* reset the client connection parameters */
                                        if (cfg->verbose)
                                                xlog(LOG_INFO, "Reusing sock=%d (old request=%d, old sock=%d) %s/%s\n",
                                                     server_socket, req.id, client_socket, host, req.http_infos.hostname );
                                        proxenet_close_socket(client_socket, &(ssl_context.client));
                                        free_http_infos(&(req.http_infos));
                                        client_socket = -1;
                                }

                                proxenet_xclean( old_req, sizeof(request_t) );
                                proxenet_xfree( host );
                        }

                        req.type = REQUEST;
                        req.id = get_new_request_id();

                        /* is connection to server not established ? -> new request */
                        if ( client_socket < 0) {
                                retcode = create_http_socket(&req, &server_socket, &client_socket, &ssl_context);
                                if (retcode < 0) {
                                        xlog(LOG_ERROR, "Failed to create %s->server socket\n", PROGNAME);
                                        proxenet_xfree(req.data);
                                        req.id = 0;
                                        break;
                                }


                                if (ssl_context.use_ssl) {
                                        req.is_ssl = true;

                                        if (req.do_intercept == false) {
#ifdef DEBUG
                                                xlog(LOG_DEBUG, "SSL interception client <-> %s <-> server disabled\n", PROGNAME);
#endif
                                                proxenet_xfree(req.data);
                                                req.type = REQUEST;
                                                req.id = get_new_request_id();
                                                continue;

                                        } else if (ssl_context.server.is_valid && ssl_context.client.is_valid) {
#ifdef DEBUG
                                                xlog(LOG_DEBUG, "SSL interception client <-> %s <-> server established\n", PROGNAME);
#endif
                                                proxenet_xfree(req.data);
                                                is_new_http_connection = true;
                                                continue;
                                        }

                                        xlog(LOG_ERROR, "%s\n", "Failed to establish interception");
                                        proxenet_xfree(req.data);
                                        break;
                                }

                                is_new_http_connection = true;
                        }



                        /* if proxenet does not relay to another proxy */
                        if (!cfg->proxy.host) {

                                if (is_new_http_connection) {

                                        if (is_ssl) {
                                                /*
                                                 * SSL request fields still have the values gathered in the CONNECT
                                                 * Those values must be updated to reflect the real request
                                                 */
                                                free_http_infos(&(req.http_infos));
                                                retcode = update_http_infos(&req);
                                        } else {
                                                /*
                                                 * Format requests
                                                 * GET http://foo/bar.blah HTTP/1.1 ...
                                                 * into
                                                 * GET /bar.blah HTTP/1.1 ...
                                                 */
                                                retcode = format_http_request(&req.data, &req.size);
                                        }
                                } else {
                                        /* if here, at least 1 request has been to server */
                                        /* so simply forward  */
                                        /* e.g. using HTTP/1.1 100 Continue */
#ifdef DEBUG
                                        xlog(LOG_DEBUG, "Resuming stream '%d'->'%d'\n", client_socket, server_socket);
#endif
                                        free_http_infos(&(req.http_infos));
                                        retcode = update_http_infos(&req);
                                }

                                if (retcode < 0){
                                        xlog(LOG_ERROR, "Failed to update %s information in request %d\n",
                                             is_ssl?"HTTPS":"HTTP", req.id);
                                        proxenet_xfree(req.data);
                                        break;
                                }
                        }


                        if(cfg->ie_compat){
                                if (is_ssl)
                                        retcode = ie_compat_read_post_body(server_socket, &req, &(ssl_context.server.context));
                                else
                                        retcode = ie_compat_read_post_body(server_socket, &req, NULL);
                                if (retcode < 0){
                                        xlog(LOG_ERROR, "%s\n", "Extending IE POST: failed");
                                        proxenet_xfree(req.data);
                                        break;
                                }
                        }


                        /* apply plugins for requests (from browser to server) */
                        if (cfg->verbose) {
                                xlog(LOG_INFO, "%s request to '%s:%d'\n",
                                     is_ssl?"SSL":"plain", req.http_infos.hostname, req.http_infos.port);

                                if (cfg->verbose > 1)
                                        xlog(LOG_INFO, "%s %s %s\n",
                                             req.http_infos.method, req.http_infos.uri, req.http_infos.version);
                        }

#ifdef DEBUG
                        xlog(LOG_DEBUG, "Request %d pre-plugins: buflen:%lu\n",
                             req.id, req.size);
#endif
                        /* hook request with all plugins in plugins_list  */
                        if ( proxenet_apply_plugins(&req) < 0) {
                                /* extremist action: any error on any plugin discard the whole request */
                                proxenet_xfree( req.data );
                                break;
                        }
#ifdef DEBUG
                        xlog(LOG_DEBUG, "Request %d post-plugins: buflen:%lu\n",
                             req.id, req.size);

                        if(cfg->verbose > 2)
                                proxenet_hexdump(req.data, req.size);
#endif

                send_to_server:
                        /* send modified data */
                        if (is_ssl && req.do_intercept) {
                                retcode = proxenet_ssl_write(&(ssl_context.client.context), req.data, req.size);
                        } else {
                                retcode = proxenet_write(client_socket, req.data, req.size);
                        }

                        /* reset data */
                        proxenet_xfree(req.data);
                        req.size = 0;

                        if (retcode < 0) {
                                xlog(LOG_ERROR, "[%d] %s\n", req.id, "Failed to write to server");
                                break;
                        }

#ifdef DEBUG
                        xlog(LOG_DEBUG, "Written %d bytes to server (socket=%s socket #%d)\n",
                             retcode, is_ssl?"SSL":"PLAIN", client_socket);
#endif

                } /* end FD_ISSET(data_from_browser) */


                /* is there data from remote server to proxy ? */
                if( client_socket > 0 && FD_ISSET(client_socket, &rfds ) ) {

                        if(req.is_ssl && req.do_intercept) {
                                n = proxenet_read_all(client_socket, &req.data, &ssl_context.client.context);
                        } else {
                                n = proxenet_read_all(client_socket, &req.data, NULL);
                        }

                        if (n < 0){
                                xlog(LOG_ERROR, "read() %s on cli_sock=#%d failed: %d\n",
                                     is_ssl?"SSL":"PLAIN",
                                     client_socket, n);
                                break;
                        }

                        if (n==0){
#ifdef DEBUG
                                xlog(LOG_DEBUG, "Socket EOF from server (cli_sock=#%d)\n",
                                     client_socket);
#endif
                                break;
                        }

                        /* update request data structure */
                        req.type   = RESPONSE;

                        /* from here, n can only be positive */
                        req.size   = (size_t) n;
                        bytes_recv += n;

                        if (req.do_intercept==false){
#ifdef DEBUG
                                xlog(LOG_DEBUG, "Intercept disabled for '%s'->browser\n", req.http_infos.hostname);
#endif
                                goto send_to_client;
                        }

                        /* apply plugins for response (from server to browser) */
#ifdef DEBUG
                        xlog(LOG_DEBUG, "Response %d pre-plugins: buflen:%lu\n",
                             req.id, req.size);
#endif
                        /* execute response hooks */
                        if ( proxenet_apply_plugins(&req) < 0) {
                                /* extremist action: any error on any plugin discard the whole request */
                                proxenet_xfree(req.data);
                                break;
                        }
#ifdef DEBUG
                        xlog(LOG_DEBUG, "Response %d post-plugins: buflen:%lu\n",
                             req.id, req.size);

                        if(cfg->verbose > 2)
                                proxenet_hexdump(req.data, req.size);
#endif

                send_to_client:
                        /* send modified data to client */
                        if (req.is_ssl && req.do_intercept)
                                retcode = proxenet_ssl_write(&(ssl_context.server.context), req.data, req.size);
                        else
                                retcode = proxenet_write(server_socket, req.data, req.size);

                        if (retcode < 0) {
                                xlog(LOG_ERROR, "[%d] %s\n", req.id, "proxy->client: write failed");
                                proxenet_xfree(req.data);
                                break;
                        }

#ifdef DEBUG
                        xlog(LOG_DEBUG, "Written %d bytes to browser (socket=%s socket #%d)\n",
                             retcode, is_ssl?"SSL":"PLAIN", client_socket);
#endif
                        proxenet_xfree(req.data);

                }  /* end FD_ISSET(data_from_server) */

        }  /* end for(;;) { select() } */


        if (req.id) {
                if (cfg->verbose)
                        xlog(LOG_INFO, "End of request %d, cleaning context\n", req.id);

                free_http_infos(&(req.http_infos));
        }


        /* close client socket */
        if (client_socket > 0) {
                if (cfg->verbose >= 2)
                        xlog(LOG_INFO, "Closing %s->server (fd=#%d)\n", PROGNAME, client_socket);

                proxenet_close_socket(client_socket, &(ssl_context.client));
        }


        /* close local socket */
        if (server_socket > 0) {
                if (cfg->verbose >= 2)
                        xlog(LOG_INFO, "Closing browser->%s (fd=#%d)\n", PROGNAME, server_socket);

                proxenet_close_socket(server_socket, &(ssl_context.server));
        }


#ifdef DEBUG
        xlog(LOG_DEBUG, "Request %d: Structures closed, leaving\n", req.id);
#endif
        /* and that's all folks */
        return;
}
Exemplo n.º 9
0
/**
 * This function updates all the fields of the current request_t with the new values found in the
 * request. Since those values will be useful many times, they are strdup-ed in a specific structure
 * (http_request_t). Those values *must* be free-ed later on.
 *
 * @return 0 if successful, -1 if any error occurs.
 */
int update_http_infos(request_t *req)
{
        char *ptr, *buf, c;

        buf = req->data;

        /* method */
        ptr = strchr(buf, ' ');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP method in request");
                if (cfg->verbose)
                        xlog(LOG_ERROR, "Buffer sent:\n%s\n", buf);
                return -1;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.method = proxenet_xstrdup2(buf);
        if (!req->http_infos.method){
                xlog(LOG_ERROR, "%s\n", "strdup(method) failed, cannot pursue...");
                return -1;
        }

        *ptr = c;
        if (!strcmp(req->http_infos.method, "CONNECT")){
                int offset;

                req->is_ssl = true;
                req->http_infos.proto = HTTPS_STRING;
                req->http_infos.port = HTTPS_DEFAULT_PORT;
                offset = ptr - buf + 1;

                if( get_hostname_from_uri(req, offset) < 0 ){
                        xlog(LOG_ERROR, "%s\n", "Failed to get hostname (URI)");
                        goto failed_hostname;
                }

                req->http_infos.path = proxenet_xstrdup2("/");
                req->http_infos.version = proxenet_xstrdup2("HTTP/1.0");

                req->http_infos.uri = get_request_full_uri(req);
                if(!req->http_infos.uri){
                        xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed");
                        goto failed_uri;
                }

                return 0;
        }


        /* hostname and port */
        if (req->is_ssl){
                req->http_infos.port = HTTPS_DEFAULT_PORT;
                req->http_infos.proto = HTTPS_STRING;
        } else {
                req->http_infos.port = HTTP_DEFAULT_PORT;
                req->http_infos.proto = HTTP_STRING;
        }

        if( get_hostname_from_header(req) < 0 ){
                xlog(LOG_ERROR, "%s\n", "Failed to get hostname (Host header)");
                goto failed_hostname;
        }


        /* path */
        buf = ptr+1;

        if (!strncmp(buf, HTTP_PROTO_STRING, strlen(HTTP_PROTO_STRING))){
                buf = strchr(buf + 8, '/');
        }

        ptr = strchr(buf, ' ');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP path in request");
                goto failed_path;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.path = proxenet_xstrdup2(buf);
        if (!req->http_infos.path){
                xlog(LOG_ERROR, "%s\n", "strdup(path) failed, cannot pursue...");
                goto failed_path;
        }
        *ptr = c;

        buf = ptr+1;


        /* version */
        ptr = strchr(req->data, '\r');
        if (!ptr){
                xlog(LOG_ERROR, "%s\n", "Cannot find HTTP version");
                goto failed_version;
        }

        c = *ptr;
        *ptr = '\0';
        req->http_infos.version = proxenet_xstrdup2(buf);
        if (!req->http_infos.version){
                xlog(LOG_ERROR, "%s\n", "strdup(version) failed, cannot pursue...");
                goto failed_version;
        }
        *ptr = c;


        /* refresh uri */
        req->http_infos.uri = get_request_full_uri(req);
        if(!req->http_infos.uri){
                xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed");
                goto failed_uri;
        }


        if (cfg->verbose) {
                xlog(LOG_INFO, "New request %d to '%s'\n",
                     req->id, req->http_infos.uri);

                if (cfg->verbose >= 2) {
                        xlog(LOG_INFO,
                             "Request HTTP information:\n"
                             "method=%s\n"
                             "proto=%s\n"
                             "hostname=%s\n"
                             "port=%d\n"
                             "path=%s\n"
                             "version=%s\n"
                             ,
                             req->http_infos.method,
                             req->http_infos.proto,
                             req->http_infos.hostname,
                             req->http_infos.port,
                             req->http_infos.path,
                             req->http_infos.version);
                }
        }

        return 0;


failed_uri:
        proxenet_xfree(req->http_infos.version);

failed_version:
        proxenet_xfree(req->http_infos.path);

failed_path:
        proxenet_xfree(req->http_infos.hostname);

failed_hostname:
        proxenet_xfree(req->http_infos.method);

        return -1;
}