Esempio n. 1
0
static void server_recv_cb(EV_P_ ev_io *w, int revents)
{
    struct server_ctx *server_recv_ctx = (struct server_ctx *)w;
    struct server *server = server_recv_ctx->server;
    struct remote *remote = NULL;

    int len = server->buf_len;
    char **buf = &server->buf;

    ev_timer_again(EV_A_ & server->recv_ctx->watcher);

    if (server->stage != 0) {
        remote = server->remote;
        buf = &remote->buf;
        len = 0;
    }

    ssize_t r = recv(server->fd, *buf + len, BUF_SIZE - len, 0);

    if (r == 0) {
        // connection closed
        if (verbose) {
            LOGI("server_recv close the connection");
        }
        close_and_free_remote(EV_A_ remote);
        close_and_free_server(EV_A_ server);
        return;
    } else if (r == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // no data
            // continue to wait for recv
            return;
        } else {
            ERROR("server recv");
            close_and_free_remote(EV_A_ remote);
            close_and_free_server(EV_A_ server);
            return;
        }
    }

    tx += r;

    // handle incomplete header
    if (server->stage == 0) {
        r += server->buf_len;
        if (r <= enc_get_iv_len()) {
            // wait for more
            if (verbose) {
#ifdef __MINGW32__
                LOGI("imcomplete header: %u", r);
#else
                LOGI("imcomplete header: %zu", r);
#endif
            }
            server->buf_len = r;
            return;
        } else {
            server->buf_len = 0;
        }
    }

    *buf = ss_decrypt(BUF_SIZE, *buf, &r, server->d_ctx);

    if (*buf == NULL) {
        LOGE("invalid password or cipher");
        report_addr(server->fd);
        close_and_free_remote(EV_A_ remote);
        close_and_free_server(EV_A_ server);
        return;
    }

    // handshake and transmit data
    if (server->stage == 5) {
        if (server->auth
                && !ss_check_crc(remote->buf, &r, server->crc_buf, &server->crc_idx)) {
            LOGE("crc error");
            report_addr(server->fd);
            close_and_free_server(EV_A_ server);
            close_and_free_remote(EV_A_ remote);
            return;
        }
        int s = send(remote->fd, remote->buf, r, 0);
        if (s == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // no data, wait for send
                remote->buf_len = r;
                remote->buf_idx = 0;
                ev_io_stop(EV_A_ & server_recv_ctx->io);
                ev_io_start(EV_A_ & remote->send_ctx->io);
            } else {
                ERROR("server_recv_send");
                close_and_free_remote(EV_A_ remote);
                close_and_free_server(EV_A_ server);
            }
        } else if (s < r) {
            remote->buf_len = r - s;
            remote->buf_idx = s;
            ev_io_stop(EV_A_ & server_recv_ctx->io);
            ev_io_start(EV_A_ & remote->send_ctx->io);
        }
        return;

    } else if (server->stage == 0) {

        /*
         * Shadowsocks TCP Relay Protocol:
         *
         *    +------+----------+----------+-----------------+
         *    | ATYP | DST.ADDR | DST.PORT | AUTH (Optional) |
         *    +------+----------+----------+-----------------+
         *    |  1   | Variable |    2     |       16        |
         *    +------+----------+----------+-----------------+
         *
         *    If ATYP & ONETIMEAUTH_FLAG(0x10) == 1, AUTH and CRC are enabled.
         */

        /*
         * Shadowsocks TCP Request Payload CRC (Optional, no CRC for response's payload):
         *
         *    +------+------+------+------+------+
         *    | DATA | CRC8 | DATA | CRC8 |     ...
         *    +------+------+------+------+------+
         *    | 128  |  1   | 128  |  1   |     ...
         *    +------+------+------+------+------+
         */

        int offset = 0;
        int need_query = 0;
        char atyp = server->buf[offset++];
        char host[256] = { 0 };
        uint16_t port = 0;
        struct addrinfo info;
        struct sockaddr_storage storage;
        memset(&info, 0, sizeof(struct addrinfo));
        memset(&storage, 0, sizeof(struct sockaddr_storage));

        // get remote addr and port
        if ((atyp & ADDRTYPE_MASK) == 1) {
            // IP V4
            struct sockaddr_in *addr = (struct sockaddr_in *)&storage;
            size_t in_addr_len = sizeof(struct in_addr);
            addr->sin_family = AF_INET;
            if (r > in_addr_len) {
                addr->sin_addr = *(struct in_addr *)(server->buf + offset);
                dns_ntop(AF_INET, (const void *)(server->buf + offset),
                         host, INET_ADDRSTRLEN);
                offset += in_addr_len;
            } else {
                LOGE("invalid header with addr type %d", atyp);
                report_addr(server->fd);
                close_and_free_server(EV_A_ server);
                return;
            }
            addr->sin_port = *(uint16_t *)(server->buf + offset);
            info.ai_family = AF_INET;
            info.ai_socktype = SOCK_STREAM;
            info.ai_protocol = IPPROTO_TCP;
            info.ai_addrlen = sizeof(struct sockaddr_in);
            info.ai_addr = (struct sockaddr *)addr;
        } else if ((atyp & ADDRTYPE_MASK) == 3) {
            // Domain name
            uint8_t name_len = *(uint8_t *)(server->buf + offset);
            if (name_len < r) {
                memcpy(host, server->buf + offset + 1, name_len);
                offset += name_len + 1;
            } else {
                LOGE("invalid name length: %d", name_len);
                report_addr(server->fd);
                close_and_free_server(EV_A_ server);
                return;
            }
            struct cork_ip ip;
            if (cork_ip_init(&ip, host) != -1) {
                info.ai_socktype = SOCK_STREAM;
                info.ai_protocol = IPPROTO_TCP;
                if (ip.version == 4) {
                    struct sockaddr_in *addr = (struct sockaddr_in *)&storage;
                    dns_pton(AF_INET, host, &(addr->sin_addr));
                    addr->sin_port = *(uint16_t *)(server->buf + offset);
                    addr->sin_family = AF_INET;
                    info.ai_family = AF_INET;
                    info.ai_addrlen = sizeof(struct sockaddr_in);
                    info.ai_addr = (struct sockaddr *)addr;
                } else if (ip.version == 6) {
                    struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage;
                    dns_pton(AF_INET6, host, &(addr->sin6_addr));
                    addr->sin6_port = *(uint16_t *)(server->buf + offset);
                    addr->sin6_family = AF_INET6;
                    info.ai_family = AF_INET6;
                    info.ai_addrlen = sizeof(struct sockaddr_in6);
                    info.ai_addr = (struct sockaddr *)addr;
                }
            } else {
                need_query = 1;
            }
        } else if ((atyp & ADDRTYPE_MASK) == 4) {
            // IP V6
            struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage;
            size_t in6_addr_len = sizeof(struct in6_addr);
            addr->sin6_family = AF_INET6;
            if (r > in6_addr_len) {
                addr->sin6_addr = *(struct in6_addr *)(server->buf + offset);
                dns_ntop(AF_INET6, (const void *)(server->buf + offset),
                         host, INET6_ADDRSTRLEN);
                offset += in6_addr_len;
            } else {
                LOGE("invalid header with addr type %d", atyp);
                report_addr(server->fd);
                close_and_free_server(EV_A_ server);
                return;
            }
            addr->sin6_port = *(uint16_t *)(server->buf + offset);
            info.ai_family = AF_INET6;
            info.ai_socktype = SOCK_STREAM;
            info.ai_protocol = IPPROTO_TCP;
            info.ai_addrlen = sizeof(struct sockaddr_in6);
            info.ai_addr = (struct sockaddr *)addr;
        }

        if (offset == 1) {
            LOGE("invalid header with addr type %d", atyp);
            report_addr(server->fd);
            close_and_free_server(EV_A_ server);
            return;
        }

        if (acl && !need_query && acl_contains_ip(host)) {
            if (verbose) {
                LOGI("Access denied to %s", host);
            }
            close_and_free_server(EV_A_ server);
            return;
        }

        port = (*(uint16_t *)(server->buf + offset));

        offset += 2;

        if (auth || (atyp & ONETIMEAUTH_FLAG)) {
            if (ss_onetimeauth_verify(server->buf + offset, server->buf, offset)) {
                LOGE("authentication error %d", atyp);
                report_addr(server->fd);
                close_and_free_server(EV_A_ server);
                return;
            };
            offset += ONETIMEAUTH_BYTES;
            server->auth = 1;
        }

        if (verbose) {
            LOGI("connect to: %s:%d", host, ntohs(port));
        }

        // XXX: should handle buffer carefully
        if (r > offset) {
            server->buf_len = r - offset;
            server->buf_idx = offset;
        }

        if (server->auth
                && !ss_check_crc(server->buf + server->buf_idx, &server->buf_len, server->crc_buf, &server->crc_idx)) {
            LOGE("crc error");
            report_addr(server->fd);
            close_and_free_server(EV_A_ server);
            return;
        }

        if (!need_query) {
            struct remote *remote = connect_to_remote(&info, server);

            if (remote == NULL) {
                LOGE("connect error");
                close_and_free_server(EV_A_ server);
                return;
            } else {
                server->remote = remote;
                remote->server = server;

                // XXX: should handle buffer carefully
                if (server->buf_len > 0) {
                    memcpy(remote->buf, server->buf + server->buf_idx,
                           server->buf_len);
                    remote->buf_len = server->buf_len;
                    remote->buf_idx = 0;
                    server->buf_len = 0;
                    server->buf_idx = 0;
                }

                server->stage = 4;

                // listen to remote connected event
                ev_io_stop(EV_A_ & server_recv_ctx->io);
                ev_io_start(EV_A_ & remote->send_ctx->io);
            }
        } else {
            server->stage = 4;
            server->query = resolv_query(host, server_resolve_cb, NULL, server,
                                         port);

            ev_io_stop(EV_A_ & server_recv_ctx->io);
        }

        return;
    }
    // should not reach here
    FATAL("server context error");
}
Esempio n. 2
0
int ss_decrypt_all(buffer_t *cipher, int method, int auth, size_t capacity)
{
    if (method > TABLE) {
        size_t iv_len = enc_iv_len;
        int ret       = 1;

        if (cipher->len <= iv_len) {
            return -1;
        }

        cipher_ctx_t evp;
        cipher_context_init(&evp, method, 0);

        static buffer_t tmp = { 0, 0, 0, NULL };
        brealloc(&tmp, cipher->len, capacity);
        buffer_t *plain = &tmp;
        plain->len = cipher->len - iv_len;

        uint8_t iv[MAX_IV_LENGTH];
        memcpy(iv, cipher->array, iv_len);
        cipher_context_set_iv(&evp, iv, iv_len, 0);

        if (method >= SALSA20) {
            crypto_stream_xor_ic((uint8_t *)plain->array,
                                 (const uint8_t *)(cipher->array + iv_len),
                                 (uint64_t)(cipher->len - iv_len),
                                 (const uint8_t *)iv, 0, enc_key, method);
        } else {
            ret = cipher_context_update(&evp, (uint8_t *)plain->array, &plain->len,
                                        (const uint8_t *)(cipher->array + iv_len),
                                        cipher->len - iv_len);
        }

        if (auth || (plain->array[0] & ONETIMEAUTH_FLAG)) {
            if (plain->len > ONETIMEAUTH_BYTES) {
                ret = !ss_onetimeauth_verify(plain, iv);
                if (ret) {
                    plain->len -= ONETIMEAUTH_BYTES;
                }
            } else {
                ret = 0;
            }
        }

        if (!ret) {
            bfree(cipher);
            cipher_context_release(&evp);
            return -1;
        }

#ifdef DEBUG
        dump("PLAIN", plain->array, plain->len);
        dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len);
#endif

        cipher_context_release(&evp);

        brealloc(cipher, plain->len, capacity);
        memcpy(cipher->array, plain->array, plain->len);
        cipher->len = plain->len;

        return 0;
    } else {
        char *begin = cipher->array;
        char *ptr   = cipher->array;
        while (ptr < begin + cipher->len) {
            *ptr = (char)dec_table[(uint8_t)*ptr];
            ptr++;
        }
        return 0;
    }
}
Esempio n. 3
0
char * ss_decrypt_all(int buf_size, char *ciphertext, ssize_t *len, int method, int auth)
{
    if (method > TABLE) {
        size_t iv_len = enc_iv_len;
        size_t c_len = *len, p_len = *len - iv_len;
        int ret = 1;

        if (*len <= iv_len) {
            return NULL;
        }

        cipher_ctx_t evp;
        cipher_context_init(&evp, method, 0);

        static int tmp_len = 0;
        static char *tmp_buf = NULL;
        int buf_len = max(p_len, buf_size);
        if (tmp_len < buf_len) {
            tmp_len = buf_len;
            tmp_buf = realloc(tmp_buf, buf_len);
        }
        char *plaintext = tmp_buf;

        uint8_t iv[MAX_IV_LENGTH];
        memcpy(iv, ciphertext, iv_len);
        cipher_context_set_iv(&evp, iv, iv_len, 0);

        if (method >= SALSA20) {
            crypto_stream_xor_ic((uint8_t *)plaintext,
                                 (const uint8_t *)(ciphertext + iv_len),
                                 (uint64_t)(c_len - iv_len),
                                 (const uint8_t *)iv, 0, enc_key, method);
        } else {
            ret = cipher_context_update(&evp, (uint8_t *)plaintext, &p_len,
                                        (const uint8_t *)(ciphertext + iv_len),
                                        c_len - iv_len);
        }

        if (auth || (plaintext[0] & ONETIMEAUTH_FLAG)) {
            if (p_len > ONETIMEAUTH_BYTES) {
                char hash[ONETIMEAUTH_BYTES];
                memcpy(hash, plaintext + p_len - ONETIMEAUTH_BYTES, ONETIMEAUTH_BYTES);
                ret = !ss_onetimeauth_verify(hash, plaintext, p_len - ONETIMEAUTH_BYTES, iv);
                if (ret) {
                    p_len -= ONETIMEAUTH_BYTES;
                }
            } else {
                ret = 0;
            }
        }

        if (!ret) {
            free(ciphertext);
            cipher_context_release(&evp);
            return NULL;
        }

#ifdef DEBUG
        dump("PLAIN", plaintext, p_len);
        dump("CIPHER", ciphertext + iv_len, c_len - iv_len);
#endif

        cipher_context_release(&evp);

        if (buf_size < p_len) {
            ciphertext = realloc(ciphertext, p_len);
        }
        *len = p_len;
        memcpy(ciphertext, plaintext, *len);

        return ciphertext;
    } else {
        char *begin = ciphertext;
        while (ciphertext < begin + *len) {
            *ciphertext = (char)dec_table[(uint8_t)*ciphertext];
            ciphertext++;
        }
        return begin;
    }
}