Exemplo n.º 1
0
// forced == 1
static void socksserver_disconnect_client(socksserver* srv, int fd, int forced) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    int fdflag = 0;
    if(CONFIG_LOG && srv->log) {
        logstart();
        printfd(fd);
        LOGPUT(1, VARISL(" disconnect, forced: "), VARII(forced), NULL);
    }

    if(forced) rocksockserver_disconnect_client(&srv->serva, fd);
    client->state = SS_DISCONNECTED;
    if(client->data) {
        client->data->state = BS_UNUSED;
        client->data->start = 0;
        client->data->used = 0;
    }

    if(client->target_fd != -1) fdflag = 1;
    fd = client->target_fd;
    client->target_fd = -1;

    if(fdflag) {
        srv->clients[fdindex(fd)].target_fd = -1;
        socksserver_disconnect_client(srv, fd, 1);
    }
}
Exemplo n.º 2
0
static int socksserver_on_clientwantsdata (void* userdata, int fd) {
    socksserver* srv = (socksserver*) userdata;

    fdinfo* client = &srv->clients[fdindex(fd)];

    if(client->target_fd >= 0 && srv->clients[fdindex(client->target_fd)].state == SS_AWAITING_PIPE)
        srv->clients[fdindex(client->target_fd)].state = SS_WIRED;

    if(client->data->state == BS_WRITING)
        socksserver_write(srv, fd);

    return 0;
}
Exemplo n.º 3
0
static int socksserver_send_auth_response_i(socksserver* srv, int fd, int version, rfc1928_authmethod meth) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    client->data->buf[0] = version;
    client->data->buf[1] = meth;
    client->data->state = BS_WRITING;
    client->data->start = 0;
    client->data->used = 2;
    return socksserver_write(srv, fd);
}
Exemplo n.º 4
0
static int socksserver_read_client(socksserver* srv, int fd) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    ssize_t nbytes;

    if ((nbytes = recv(fd, client->data->buf + client->data->start, CLIENT_BUFSIZE - client->data->start, 0)) <= 0) {
        socksserver_on_clientdisconnect(srv, fd);
        rocksockserver_disconnect_client(&srv->serva, fd);
        return -1;
    }
    client->data->start += nbytes;
    client->data->used += nbytes;
    return 0;
}
Exemplo n.º 5
0
// returns either the one authmethod supported by the server or AM_INVALID.
static rfc1928_authmethod socksserver_parse_authpacket(socksserver* srv, int fd) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    unsigned char numMethods;
    unsigned char i;

    if(client->data->start < 3) return AM_INVALID;
    if(client->data->buf[0] != 5) return AM_INVALID;
    numMethods = client->data->buf[1];
    for(i = 0; i < numMethods && (2U + i) < client->data->start; i++) {
        if(client->data->buf[2 + i] == (unsigned char) srv->accepted_authmethod)
            return srv->accepted_authmethod;
    }
    return AM_INVALID;
}
Exemplo n.º 6
0
// return 0 when packet is not complete, 1 if successfull, -1 if not
static int socksserver_check_credentials(socksserver* srv, int fd) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    size_t i = 0;
    unsigned char ulen, plen;
    unsigned char* buf = client->data->buf;
    if(client->data->start < 5) return 0;
    if(buf[i++] != 1) return -1;
    ulen = buf[i++];
    if(client->data->start < i + ulen + 2) return 0; // passwd must be at least 1 char long
    if(ulen != srv->username.size || memcmp(buf + i, srv->username.ptr, ulen)) return -1;
    i += ulen;
    plen = buf[i++];
    if(client->data->start < i + plen) return 0;
    if(plen != srv->password.size || memcmp(buf + i, srv->password.ptr, plen)) return -1;
    return 1;
}
Exemplo n.º 7
0
static int socksserver_write(socksserver* srv, int fd) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    client->data->state = BS_WRITING;
    ssize_t written = send(fd, client->data->buf + client->data->start, client->data->used - client->data->start, MSG_NOSIGNAL);
    int err;

    if (written < 0) {
        err = errno;
        if(err == EAGAIN || err == EWOULDBLOCK) return 0;
        else {
            //if(err == EBADF)
            LOGP("writing");
            socksserver_disconnect_client(srv, fd, 1);
            rocksockserver_disconnect_client(&srv->serva, fd);
            return 3;
        }
    } else if ((size_t) written == client->data->used - client->data->start)
        client->data->state = BS_IDLE;
    return 0;
}
Exemplo n.º 8
0
/*
        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+
*/
static int socksserver_send_error(socksserver* srv, int fd, rfc1928_errorcode ec) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    size_t i = 0;
    client->data->buf[i++] = 5;
    client->data->buf[i++] = ec;
    client->data->buf[i++] = 0;

    client->data->buf[i++] = AT_IPV4;
    client->data->buf[i++] = 0;
    client->data->buf[i++] = 0;
    client->data->buf[i++] = 0;
    client->data->buf[i++] = 0;

    client->data->buf[i++] = 0;
    client->data->buf[i++] = 0;

    client->data->used = i;
    client->data->start = 0;
    client->data->state = BS_WRITING;
    return socksserver_write(srv, fd);
}
Exemplo n.º 9
0
static int socksserver_on_clientconnect (void* userdata, struct sockaddr_storage* clientaddr, int fd) {
    socksserver* srv = (socksserver*) userdata;
    char buffer[256];
    (void) buffer;
    if(CONFIG_LOG && srv->log && clientaddr) {
        logstart();
        printfd(fd);
        LOGPUT(1, VARISL(" connect from: "), VARIC(get_client_ip(clientaddr, buffer, sizeof(buffer))), NULL);
    }

    if(fd < 3 || fd >= MAX_FD) {
        rocksockserver_disconnect_client(&srv->serva, fd);
        return -2;
    }

    fdinfo* client = &srv->clients[fdindex(fd)];

    // put into nonblocking mode, so that writes will not block the server
    int flags = fcntl(fd, F_GETFL);
    if(flags == -1) return -1;
    if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) return -2;

    client->data = find_free_buffer(srv);
    if (!client->data) {
        if(CONFIG_LOG && srv->log) {
            logstart();
            LOGPUTS(1, SPL("warning: couldnt find free buffer\n"));
        }
        rocksockserver_disconnect_client(&srv->serva, fd);
        return -2;
    }

    client->state = SS_CONNECTED;
    client->data->state = BS_IDLE;
    client->data->start = 0;
    client->target_fd = -1;

    return 0;
}
Exemplo n.º 10
0
void* PollSelect::getUserData(int fd) const {
	natural fdindex(fd);
	if (fdindex >= socketMap.length()) return 0;
	else return socketMap[fdindex].userData;
}
Exemplo n.º 11
0
/*
  The client connects to the server, and sends a version
   identifier/method selection message:

                   +----+----------+----------+
                   |VER | NMETHODS | METHODS  |
                   +----+----------+----------+
                   | 1  |    1     | 1 to 255 |
                   +----+----------+----------+

   The VER field is set to X'05' for this version of the protocol.  The
   NMETHODS field contains the number of method identifier octets that
   appear in the METHODS field.

   The server selects from one of the methods given in METHODS, and
   sends a METHOD selection message:

                         +----+--------+
                         |VER | METHOD |
                         +----+--------+
                         | 1  |   1    |
                         +----+--------+

   If the selected METHOD is X'FF', none of the methods listed by the
   client are acceptable, and the client MUST close the connection.
*/
static int socksserver_on_clientread (void* userdata, int fd, size_t dummy) {
    socksserver* srv = (socksserver*) userdata;
    fdinfo* client = &srv->clients[fdindex(fd)];
    rfc1928_authmethod authmethod;
    char buf[4];
    ssize_t readv;
    int ret;

    (void) dummy;

    if(client->state == SS_AWAITING_PIPE || (client->data->state != BS_IDLE && client->data->state != BS_READING)) {
        if((readv = recvfrom(fd, buf, 4, MSG_PEEK, NULL, NULL)) <= 0) {
            if (!readv)
                socksserver_on_clientdisconnect(userdata, fd);
            else
                LOGP("recvfrom");
            rocksockserver_disconnect_client(&srv->serva, fd);
        }
        return 1;
    }

    if(client->state == SS_WIRED || client->state == SS_SOCKSTARGET) {
        client->data->start = 0;
        client->data->used = 0;
    }

    if(socksserver_read_client(srv, fd))
        return 2;

    switch(client->state) {
    case SS_AWAITING_DISCONNECT:
        socksserver_disconnect_client(srv, fd, 1);
        break;
    case SS_CONNECTED:
        if((authmethod = socksserver_parse_authpacket(srv, fd)) != AM_INVALID) {
            if(authmethod == AM_USERNAME)
                client->state = SS_AWAITING_AUTH_PACKET;
            else
                client->state = SS_AUTHED;
        } else {
            client->state = SS_AWAITING_DISCONNECT;
        }
        socksserver_send_auth_response(srv, fd, authmethod);
        break;
    case SS_AWAITING_AUTH_PACKET:
        ret = socksserver_check_credentials(srv, fd);
        if (!ret) return 3;
        if (ret == -1) {
            client->state = SS_AWAITING_DISCONNECT;
            socksserver_send_auth_response_i(srv, fd, 1, AM_INVALID);
        } else {
            client->state = SS_AUTHED;
            socksserver_send_auth_response_i(srv, fd, 1, 0);
        }
        break;
    case SS_AUTHED:
        ret = socksserver_connect_request(srv, fd);
        if(ret == -1) return 4;
        if(ret) {
            client->state = SS_AWAITING_DISCONNECT;
        } else {
            client->state = SS_AWAITING_PIPE;
        }
        socksserver_send_error(srv, fd, ret);
        break;
    case SS_SOCKSTARGET:
        if(srv->clients[fdindex(client->target_fd)].state == SS_AWAITING_PIPE)
            srv->clients[fdindex(client->target_fd)].state = SS_WIRED;
        client->data->start = 0;
        socksserver_write(srv, client->target_fd);
        break;
    case SS_WIRED:
        client->data->start = 0;
        socksserver_write(srv, client->target_fd);
        break;
    default:
        break;
    }
    return 0;
}
Exemplo n.º 12
0
static int socksserver_connect_request(socksserver* srv, int fd) {
    fdinfo* client = &srv->clients[fdindex(fd)];
    size_t i = 0;
    unsigned char dlen = 0;
    unsigned char* buf = client->data->buf;
    int flags, ret;
    host_info addr;

    struct addrinfo addrbuf;
    struct sockaddr sockbuf;

    memset(&addr, 0, sizeof(addr));
    memset(&addrbuf, 0, sizeof(addrbuf));
    memset(&sockbuf, 0, sizeof(sockbuf));

    addrbuf.ai_addr = &sockbuf;
    addr.hostaddr = &addrbuf;


    if(!client->data->start) return -1;
    if(buf[i++] != 5) return EC_NOT_ALLOWED; // check first byte whenever the message length is > 0 to not waste resources on maldoers
    if(client->data->start < 1+1+1+1+4+2) return -1;

    if(buf[i++] != 1) return EC_COMMAND_NOT_SUPPORTED; // we support only the connect method.
    if(buf[i++] != 0) return EC_GENERAL_FAILURE;
    switch(buf[i++]) {
    case 1:
        //ipv4
        memcpy(&((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_addr, buf + 4, 4);
        memcpy(&((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_port, buf + 8, 2);
        ((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_family = PF_INET;
        addr.hostaddr->ai_addr->sa_family = PF_INET;
        addr.hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
        break;
    case 3:
        //dns
        if(CONFIG_DNS) {
            dlen = buf[i++];
            if(client->data->start < 1U+1U+1U+1U+1U+dlen+2U) return -1;
            addr.port = my_ntohs(buf + i + dlen);
            buf[i + dlen] = 0;
            addr.host = (char*) (buf + i);
            if(CONFIG_IPV6) addr.hostaddr = NULL;
            if(!resolve_host(&addr)) {
                if(CONFIG_IPV6) {
                    memcpy(&addrbuf, addr.hostaddr, sizeof(struct addrinfo));
                    freeaddrinfo(addr.hostaddr);
                    addr.hostaddr = &addrbuf;
                }
            } else goto notsupported;
            break;
        } else goto notsupported;
    case 4: //ipv6
        if(CONFIG_IPV6) {
            if(client->data->start < 1+1+1+1+16+2) return -1;
            memcpy(&((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_addr, buf + 4, 16);
            memcpy(&((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_port, buf + 20, 2);
            ((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_family = PF_INET6;
            addr.hostaddr->ai_addr->sa_family = PF_INET6;
            addr.hostaddr->ai_addrlen = sizeof(struct sockaddr_in6);
            break;
        }
    default:
notsupported:
        return EC_ADDRESSTYPE_NOT_SUPPORTED;
    }
    client->target_fd = socket(addr.hostaddr->ai_addr->sa_family, SOCK_STREAM, 0);
    if(client->target_fd == -1) {
neterror:
        switch(errno) {
        case ENETDOWN:
        case ENETUNREACH:
        case ENETRESET:
            return EC_NET_UNREACHABLE;
        case EHOSTUNREACH:
        case EHOSTDOWN:
            return EC_HOST_UNREACHABLE;
        case ECONNREFUSED:
            return EC_CONN_REFUSED;
        default:
            return EC_GENERAL_FAILURE;
        }
    }

    if(client->target_fd >= MAX_FD) {
        close(client->target_fd);
        return EC_GENERAL_FAILURE;
    }

    flags = fcntl(client->target_fd, F_GETFL);
    if(flags == -1) return EC_GENERAL_FAILURE;

    if(fcntl(client->target_fd, F_SETFL, flags | O_NONBLOCK) == -1) return EC_GENERAL_FAILURE;

    ret = connect(client->target_fd, addr.hostaddr->ai_addr, addr.hostaddr->ai_addrlen);
    if(ret == -1) {
        ret = errno;
        if (!(ret == EINPROGRESS || ret == EWOULDBLOCK))
            goto neterror;
    }

    srv->clients[fdindex(client->target_fd)].state = SS_SOCKSTARGET;
    srv->clients[fdindex(client->target_fd)].data = client->data;
    srv->clients[fdindex(client->target_fd)].target_fd = fd;
    rocksockserver_watch_fd(&srv->serva, client->target_fd);

    if(CONFIG_LOG && srv->log) {
        if(get_client_ip((struct sockaddr_storage*) addr.hostaddr->ai_addr, (char*) buf, CLIENT_BUFSIZE)) {
            logstart();
            printfd(fd);
            LOGPUTS(1, SPLITERAL(" -> "));
            printfd(client->target_fd);
            LOGPUT(1, VARISL(" <"), VARIC((char*)buf), VARISL(">"), NULL);
        }
    }

    return EC_SUCCESS;
}