diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3f0989..6c0c373 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,7 +95,7 @@ else() endif() add_executable(rtty main.c utils.c buffer/buffer.c log.c net.c net.h rtty.c rtty.h command.c - file.c upfile.c upfile.h downfile.c downfile.h ssl.c ssl.h) + file.c upfile.c upfile.h downfile.c downfile.h ssl.c ssl.h web.c) target_link_libraries(rtty ${EXTRA_LIBS}) # configure a header file to pass some of the CMake settings to the source code diff --git a/src/buffer b/src/buffer index 2fb5523..1bd1b6d 160000 --- a/src/buffer +++ b/src/buffer @@ -1 +1 @@ -Subproject commit 2fb5523d44eb5cc65eb9473ecbf689727097b3cd +Subproject commit 1bd1b6d192eda9e5bc24582e7ef3a4a017f9b064 diff --git a/src/net.c b/src/net.c index 5bc4241..69283ac 100644 --- a/src/net.c +++ b/src/net.c @@ -22,13 +22,15 @@ * SOFTWARE. */ -#include #include #include +#include #include #include #include +#include +#include "list.h" #include "net.h" #include "log.h" @@ -56,7 +58,7 @@ static const char *port2str(int port) static void sock_write_cb(struct ev_loop *loop, struct ev_io *w, int revents) { - struct net_context *ctx = w->data; + struct net_context *ctx = container_of(w, struct net_context, iow); int err = 0; socklen_t len = sizeof(err); int ret; @@ -76,40 +78,69 @@ static void sock_write_cb(struct ev_loop *loop, struct ev_io *w, int revents) } ctx->on_connected(w->fd, ctx->arg); + free(ctx); return; err: close(w->fd); ctx->on_connected(-1, ctx->arg); + free(ctx); } static void timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents) { - struct net_context *ctx = w->data; + struct net_context *ctx = container_of(w, struct net_context, tmr); log_err("network connect timeout\n"); ev_io_stop(loop, &ctx->iow); close(ctx->sock); ctx->on_connected(-1, ctx->arg); + free(ctx); } static void wait_connect(struct ev_loop *loop, int sock, int timeout, void (*on_connected)(int sock, void *arg), void *arg) { - static struct net_context ctx; + struct net_context *ctx = calloc(1, sizeof(struct net_context)); - ctx.sock = sock; - ctx.arg = arg; - ctx.on_connected = on_connected; + ctx->sock = sock; + ctx->arg = arg; + ctx->on_connected = on_connected; - ev_timer_init(&ctx.tmr, timer_cb, timeout, 0); - ctx.tmr.data = &ctx; - ev_timer_start(loop, &ctx.tmr); + ev_timer_init(&ctx->tmr, timer_cb, timeout, 0); + ev_timer_start(loop, &ctx->tmr); - ev_io_init(&ctx.iow, sock_write_cb, sock, EV_WRITE); - ctx.iow.data = &ctx; - ev_io_start(loop, &ctx.iow); + ev_io_init(&ctx->iow, sock_write_cb, sock, EV_WRITE); + ev_io_start(loop, &ctx->iow); +} + +int tcp_connect_sockaddr(struct ev_loop *loop, const struct sockaddr *addr, socklen_t addrlen, + void (*on_connected)(int sock, void *arg), void *arg) +{ + int sock; + + sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (sock < 0) { + log_err("create socket failed: %s\n", strerror(errno)); + return -1; + } + + if (connect(sock, addr, addrlen) < 0) { + if (errno != EINPROGRESS) { + log_err("connect failed: %s\n", strerror(errno)); + goto err; + } + wait_connect(loop, sock, 3, on_connected, arg); + } else { + on_connected(sock, arg); + } + + return sock; + +err: + close(sock); + return -1; } int tcp_connect(struct ev_loop *loop, const char *host, int port, @@ -123,7 +154,7 @@ int tcp_connect(struct ev_loop *loop, const char *host, int port, .ai_flags = AI_ADDRCONFIG }; int sock = -1; - int addr_len; + int addrlen; int ret; ret = getaddrinfo(host, port2str(port), &hints, &result); @@ -140,7 +171,7 @@ int tcp_connect(struct ev_loop *loop, const char *host, int port, for (rp = result; rp != NULL; rp = rp->ai_next) { if (rp->ai_family == AF_INET) { addr = rp->ai_addr; - addr_len = rp->ai_addrlen; + addrlen = rp->ai_addrlen; break; } } @@ -150,23 +181,7 @@ int tcp_connect(struct ev_loop *loop, const char *host, int port, goto free_addrinfo; } - sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - if (sock < 0) { - log_err("create socket failed: %s\n", strerror(errno)); - goto free_addrinfo; - } - - if (connect(sock, addr, addr_len) < 0) { - if (errno != EINPROGRESS) { - log_err("connect failed: %s\n", strerror(errno)); - close(sock); - sock = -1; - goto free_addrinfo; - } - wait_connect(loop, sock, 3, on_connected, arg); - } else { - on_connected(sock, arg); - } + sock = tcp_connect_sockaddr(loop, addr, addrlen, on_connected, arg); free_addrinfo: freeaddrinfo(result); diff --git a/src/net.h b/src/net.h index 3c41a79..1a40d7c 100644 --- a/src/net.h +++ b/src/net.h @@ -25,9 +25,13 @@ #ifndef RTTY_NET_H #define RTTY_NET_H +#include #include int tcp_connect(struct ev_loop *loop, const char *host, int port, void (*on_connected)(int sock, void *arg), void *arg); +int tcp_connect_sockaddr(struct ev_loop *loop, const struct sockaddr *addr, socklen_t addrlen, + void (*on_connected)(int sock, void *arg), void *arg); + #endif diff --git a/src/rtty.c b/src/rtty.c index 83ea782..6e9e1d8 100644 --- a/src/rtty.c +++ b/src/rtty.c @@ -30,6 +30,7 @@ #include "ssl.h" #include "net.h" #include "log.h" +#include "web.h" #include "file.h" #include "rtty.h" #include "list.h" @@ -330,6 +331,10 @@ static void parse_msg(struct rtty *rtty) parse_file_msg(&rtty->file_context, buffer_pull_u8(rb), rb, msglen - 1); break; + case MSG_TYPE_WEB: + web_request(rtty, msglen); + break; + default: log_err("invalid message type: %d\n", msgtype); rtty_exit(rtty); diff --git a/src/rtty.h b/src/rtty.h index 128c13e..b0d90f6 100644 --- a/src/rtty.h +++ b/src/rtty.h @@ -42,7 +42,8 @@ enum { MSG_TYPE_WINSIZE = 0x04, MSG_TYPE_CMD = 0x05, MSG_TYPE_HEARTBEAT = 0x06, - MSG_TYPE_FILE = 0x07 + MSG_TYPE_FILE = 0x07, + MSG_TYPE_WEB = 0x08 }; struct rtty; diff --git a/src/web.c b/src/web.c new file mode 100644 index 0000000..b79b747 --- /dev/null +++ b/src/web.c @@ -0,0 +1,222 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "web.h" +#include "net.h" +#include "log.h" + +struct web_request_ctx { + struct list_head head; + struct rtty *rtty; + struct ev_timer tmr; + struct ev_io ior; + struct ev_io iow; + struct buffer rb; + struct buffer wb; + ev_tstamp active; + bool closed; + int sock; + int id; +}; + +static LIST_HEAD(reqs); + +static void web_request_free(struct web_request_ctx *ctx) +{ + struct rtty *rtty = ctx->rtty; + struct ev_loop *loop = rtty->loop; + struct buffer *wb = &rtty->wb; + + if (ctx->sock > 0) { + ev_io_stop(loop, &ctx->ior); + ev_io_stop(loop, &ctx->iow); + ev_timer_stop(loop, &ctx->tmr); + close(ctx->sock); + } + + buffer_put_u8(wb, MSG_TYPE_WEB); + buffer_put_u16be(wb, 2); + buffer_put_u16be(wb, ctx->id); + ev_io_start(loop, &rtty->iow); + + buffer_free(&ctx->rb); + buffer_free(&ctx->wb); + + list_del(&ctx->head); + + free(ctx); +} + +static void on_net_read(struct ev_loop *loop, struct ev_io *w, int revents) +{ + struct web_request_ctx *ctx = container_of(w, struct web_request_ctx, ior); + struct rtty *rtty = ctx->rtty; + struct buffer *wb = &rtty->wb; + uint8_t buf[4096]; + int ret; + + ret = read(w->fd, buf, 4096); + if (ret <= 0) + goto done; + + buffer_put_u8(wb, MSG_TYPE_WEB); + buffer_put_u16be(wb, 2 + ret); + buffer_put_u16be(wb, ctx->id); + buffer_put_data(wb, buf, ret); + ev_io_start(rtty->loop, &rtty->iow); + + ctx->active = ev_now(rtty->loop); + + return; + +done: + web_request_free(ctx); +} + +static void on_net_write(struct ev_loop *loop, struct ev_io *w, int revents) +{ + struct web_request_ctx *ctx = container_of(w, struct web_request_ctx, iow); + + if (buffer_pull_to_fd(&ctx->wb, w->fd, -1) < 0) + goto err; + + if (buffer_length(&ctx->wb) > 0) + return; + +err: + if (ctx->closed) { + web_request_free(ctx); + return; + } + + ev_io_stop(loop, w); +} + +static void on_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents) +{ + struct web_request_ctx *ctx = container_of(w, struct web_request_ctx, tmr); + ev_tstamp now = ev_now(loop); + + if (now - ctx->active < 30) + return; + + web_request_free(ctx); +} + +static void on_connected(int sock, void *arg) +{ + struct web_request_ctx *ctx = (struct web_request_ctx *)arg; + struct ev_loop *loop = ctx->rtty->loop; + + if (sock < 0) { + web_request_free(ctx); + return; + } + + ev_io_init(&ctx->ior, on_net_read, sock, EV_READ); + ev_io_start(loop, &ctx->ior); + + ev_io_init(&ctx->iow, on_net_write, sock, EV_WRITE); + ev_io_start(loop, &ctx->iow); + + ev_timer_init(&ctx->tmr, on_timer_cb, 1, 0); + ev_timer_start(loop, &ctx->tmr); + + ctx->sock = sock; +} + +static struct web_request_ctx *find_exist_ctx(int port) +{ + struct web_request_ctx *ctx; + + list_for_each_entry(ctx, &reqs, head) + if (ctx->id == port) + return ctx; + return NULL; +} + +void web_request(struct rtty *rtty, int len) +{ + struct web_request_ctx *ctx; + int id, sock, req_len; + struct sockaddr_in addrin = { + .sin_family = AF_INET + }; + void *data; + + id = buffer_pull_u16be(&rtty->rb); + req_len = len - 2; + + ctx = find_exist_ctx(id); + if (ctx) { + if (req_len == 0) { + ctx->closed = true; + if (ctx->sock > 0) + ev_io_start(rtty->loop, &ctx->iow); + return; + } + + buffer_pull(&rtty->rb, NULL, 6); + req_len -= 6; + + data = buffer_put(&ctx->wb, req_len); + buffer_pull(&rtty->rb, data, req_len); + + if (ctx->sock > 0) + ev_io_start(rtty->loop, &ctx->iow); + return; + } + + if (req_len == 0) + return; + + addrin.sin_addr.s_addr = buffer_pull_u32(&rtty->rb); + addrin.sin_port = buffer_pull_u16(&rtty->rb); + + req_len -= 6; + + ctx = (struct web_request_ctx *)calloc(1, sizeof(struct web_request_ctx)); + ctx->rtty = rtty; + ctx->id = id; + ctx->active = ev_now(rtty->loop); + + data = buffer_put(&ctx->wb, req_len); + buffer_pull(&rtty->rb, data, req_len); + + list_add(&ctx->head, &reqs); + + sock = tcp_connect_sockaddr(rtty->loop, (struct sockaddr *)&addrin, sizeof(addrin), on_connected, ctx); + if (sock < 0) + web_request_free(ctx); +} diff --git a/src/web.h b/src/web.h new file mode 100644 index 0000000..69a873f --- /dev/null +++ b/src/web.h @@ -0,0 +1,32 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _WEB_H +#define _WEB_H + +#include "rtty.h" + +void web_request(struct rtty *rtty, int len); + +#endif \ No newline at end of file