trunk/src/lib/web/mongoose.c
| r30951 | r30952 | |
| 222 | 222 | void ns_server_wakeup(struct ns_server *); |
| 223 | 223 | void ns_server_wakeup_ex(struct ns_server *, ns_callback_t, void *, size_t); |
| 224 | 224 | void ns_iterate(struct ns_server *, ns_callback_t cb, void *param); |
| 225 | struct ns_connection *ns_next(struct ns_server *, struct ns_connection *); |
| 225 | 226 | struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p); |
| 226 | 227 | |
| 227 | 228 | int ns_bind(struct ns_server *, const char *addr); |
| r30951 | r30952 | |
| 676 | 677 | #endif |
| 677 | 678 | } |
| 678 | 679 | if (flags & 2) { |
| 679 | | snprintf(buf + strlen(buf), len - (strlen(buf) + 1), ":%d", |
| 680 | | (int) ntohs(sa.sin.sin_port)); |
| 680 | snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d", |
| 681 | flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port)); |
| 681 | 682 | } |
| 682 | 683 | } |
| 683 | 684 | } |
| r30951 | r30952 | |
| 861 | 862 | tv.tv_usec = (milli % 1000) * 1000; |
| 862 | 863 | |
| 863 | 864 | if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) { |
| 865 | // select() might have been waiting for a long time, reset current_time |
| 866 | // now to prevent last_io_time being set to the past. |
| 867 | current_time = time(NULL); |
| 868 | |
| 864 | 869 | // Accept new connections |
| 865 | 870 | if (server->listening_sock != INVALID_SOCKET && |
| 866 | 871 | FD_ISSET(server->listening_sock, &read_set)) { |
| r30951 | r30952 | |
| 885 | 890 | |
| 886 | 891 | for (conn = server->active_connections; conn != NULL; conn = tmp_conn) { |
| 887 | 892 | tmp_conn = conn->next; |
| 888 | | //DBG(("%p LOOP %p", conn, conn->ssl)); |
| 889 | 893 | if (FD_ISSET(conn->sock, &read_set)) { |
| 890 | 894 | conn->last_io_time = current_time; |
| 891 | 895 | ns_read_from_socket(conn); |
| r30951 | r30952 | |
| 979 | 983 | return conn; |
| 980 | 984 | } |
| 981 | 985 | |
| 986 | struct ns_connection *ns_next(struct ns_server *s, struct ns_connection *conn) { |
| 987 | return conn == NULL ? s->active_connections : conn->next; |
| 988 | } |
| 989 | |
| 982 | 990 | void ns_iterate(struct ns_server *server, ns_callback_t cb, void *param) { |
| 983 | 991 | struct ns_connection *conn, *tmp_conn; |
| 984 | 992 | |
| r30951 | r30952 | |
| 1656 | 1664 | |
| 1657 | 1665 | static void *pull_from_stdout(void *arg) { |
| 1658 | 1666 | struct threadparam *tp = (struct threadparam *)arg; |
| 1659 | | int k=0, stop = 0; |
| 1667 | int k, stop = 0; |
| 1660 | 1668 | DWORD n, sent; |
| 1661 | 1669 | char buf[IOBUF_SIZE]; |
| 1662 | 1670 | |
| r30951 | r30952 | |
| 2704 | 2712 | free(copy); |
| 2705 | 2713 | } |
| 2706 | 2714 | |
| 2715 | // If we send closing frame, schedule a connection to be closed after |
| 2716 | // data is drained to the client. |
| 2717 | if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { |
| 2718 | MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; |
| 2719 | } |
| 2720 | |
| 2707 | 2721 | return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; |
| 2708 | 2722 | } |
| 2709 | 2723 | |
| r30951 | r30952 | |
| 2734 | 2748 | if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) { |
| 2735 | 2749 | send_websocket_handshake(conn, key); |
| 2736 | 2750 | } |
| 2751 | call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT); |
| 2737 | 2752 | } |
| 2738 | 2753 | } |
| 2739 | 2754 | |
| r30951 | r30952 | |
| 3995 | 4010 | } |
| 3996 | 4011 | #endif |
| 3997 | 4012 | |
| 3998 | | static int parse_url(const char *url, char *proto, size_t plen, |
| 3999 | | char *host, size_t hlen, unsigned short *port) { |
| 4000 | | int n; |
| 4001 | | char fmt1[100], fmt2[100], fmt3[100]; |
| 4002 | | |
| 4003 | | *port = 80; |
| 4004 | | proto[0] = host[0] = '\0'; |
| 4005 | | |
| 4006 | | snprintf(fmt1, sizeof(fmt1), "%%%zu[a-z]://%%%zu[^: ]:%%hu%%n", plen, hlen); |
| 4007 | | snprintf(fmt2, sizeof(fmt2), "%%%zu[a-z]://%%%zu[^/ ]%%n", plen, hlen); |
| 4008 | | snprintf(fmt3, sizeof(fmt3), "%%%zu[^: ]:%%hu%%n", hlen); |
| 4009 | | |
| 4010 | | if (sscanf(url, fmt1, proto, host, port, &n) == 3 || |
| 4011 | | sscanf(url, fmt2, proto, host, &n) == 2) { |
| 4012 | | return n; |
| 4013 | | } else if (sscanf(url, fmt3, host, port, &n) == 2) { |
| 4014 | | proto[0] = '\0'; |
| 4015 | | return n; |
| 4016 | | } |
| 4017 | | |
| 4018 | | return 0; |
| 4019 | | } |
| 4020 | | |
| 4021 | 4013 | static void proxy_request(struct ns_connection *pc, struct mg_connection *c) { |
| 4022 | 4014 | int i, sent_close_header = 0; |
| 4023 | 4015 | |
| r30951 | r30952 | |
| 4042 | 4034 | |
| 4043 | 4035 | } |
| 4044 | 4036 | |
| 4037 | #ifdef NS_ENABLE_SSL |
| 4038 | int mg_terminate_ssl(struct mg_connection *c, const char *cert) { |
| 4039 | static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n"; |
| 4040 | struct connection *conn = MG_CONN_2_CONN(c); |
| 4041 | int n; |
| 4042 | SSL_CTX *ctx; |
| 4043 | |
| 4044 | DBG(("%p MITM", conn)); |
| 4045 | SSL_library_init(); |
| 4046 | if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0; |
| 4047 | |
| 4048 | SSL_CTX_use_certificate_file(ctx, cert, 1); |
| 4049 | SSL_CTX_use_PrivateKey_file(ctx, cert, 1); |
| 4050 | SSL_CTX_use_certificate_chain_file(ctx, cert); |
| 4051 | |
| 4052 | // When clear-text reply is pushed to client, switch to SSL mode. |
| 4053 | n = send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0); |
| 4054 | DBG(("%p %lu %d SEND", c, sizeof(ok) - 1, n)); |
| 4055 | conn->ns_conn->send_iobuf.len = 0; |
| 4056 | conn->endpoint_type = EP_USER; // To keep-alive in close_local_endpoint() |
| 4057 | close_local_endpoint(conn); // Clean up current CONNECT request |
| 4058 | if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) { |
| 4059 | SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock); |
| 4060 | } |
| 4061 | SSL_CTX_free(ctx); |
| 4062 | return 1; |
| 4063 | } |
| 4064 | #endif |
| 4065 | |
| 4045 | 4066 | static void proxify_connection(struct connection *conn) { |
| 4046 | 4067 | char proto[10], host[500], cert[500]; |
| 4047 | 4068 | unsigned short port = 80; |
| 4048 | 4069 | struct mg_connection *c = &conn->mg_conn; |
| 4049 | 4070 | struct ns_server *server = &conn->server->ns_server; |
| 4050 | | struct ns_connection *pc; |
| 4051 | | int n, use_ssl; |
| 4071 | struct ns_connection *pc = NULL; |
| 4072 | int n = 0; |
| 4073 | const char *url = c->uri; |
| 4052 | 4074 | |
| 4053 | 4075 | proto[0] = host[0] = cert[0] = '\0'; |
| 4054 | | n = parse_url(c->uri, proto, sizeof(proto), host, sizeof(host), &port); |
| 4076 | if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 && |
| 4077 | sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 && |
| 4078 | sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) { |
| 4079 | n = 0; |
| 4080 | } |
| 4055 | 4081 | |
| 4056 | 4082 | #ifdef NS_ENABLE_SSL |
| 4057 | 4083 | // Find out whether we should be in the MITM mode |
| r30951 | r30952 | |
| 4060 | 4086 | int host_len = strlen(host); |
| 4061 | 4087 | struct vec a, b; |
| 4062 | 4088 | |
| 4063 | | while ((certs = next_option(certs, &a, &b)) != NULL) { |
| 4064 | | if (a.len == host_len && mg_strncasecmp(a.ptr, host, a.len) == 0) { |
| 4065 | | snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr); |
| 4066 | | break; |
| 4067 | | } |
| 4089 | while (conn->ns_conn->ssl == NULL && port != 80 && |
| 4090 | (certs = next_option(certs, &a, &b)) != NULL) { |
| 4091 | if (a.len != host_len || mg_strncasecmp(a.ptr, host, a.len)) continue; |
| 4092 | snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr); |
| 4093 | mg_terminate_ssl(&conn->mg_conn, cert); |
| 4094 | return; |
| 4068 | 4095 | } |
| 4069 | 4096 | } |
| 4070 | 4097 | #endif |
| 4071 | 4098 | |
| 4072 | | use_ssl = port != 80 && cert[0] != '\0'; |
| 4073 | | |
| 4074 | 4099 | if (n > 0 && |
| 4075 | | (pc = ns_connect(server, host, port, use_ssl, conn)) != NULL) { |
| 4100 | (pc = ns_connect(server, host, port, conn->ns_conn->ssl != NULL, |
| 4101 | conn)) != NULL) { |
| 4076 | 4102 | // Interlink two connections |
| 4077 | 4103 | pc->flags |= MG_PROXY_CONN; |
| 4078 | 4104 | conn->endpoint_type = EP_PROXY; |
| 4079 | 4105 | conn->endpoint.nc = pc; |
| 4080 | | DBG(("%p [%s] -> %p %d", conn, c->uri, pc, use_ssl)); |
| 4106 | DBG(("%p [%s] -> %p %p", conn, c->uri, pc, conn->ns_conn->ssl)); |
| 4081 | 4107 | |
| 4082 | 4108 | if (strcmp(c->request_method, "CONNECT") == 0) { |
| 4083 | 4109 | // For CONNECT request, reply with 200 OK. Tunnel is established. |
| r30951 | r30952 | |
| 4085 | 4111 | conn->request_len = 0; |
| 4086 | 4112 | free(conn->request); |
| 4087 | 4113 | conn->request = NULL; |
| 4088 | | #ifdef NS_ENABLE_SSL |
| 4089 | | if (use_ssl) { |
| 4090 | | SSL_CTX *ctx; |
| 4091 | | |
| 4092 | | DBG(("%s", "Triggering MITM mode: terminating SSL connection")); |
| 4093 | | SSL_library_init(); |
| 4094 | | ctx = SSL_CTX_new(SSLv23_server_method()); |
| 4095 | | |
| 4096 | | if (ctx == NULL) { |
| 4097 | | pc->flags |= NSF_CLOSE_IMMEDIATELY; |
| 4098 | | } else { |
| 4099 | | SSL_CTX_use_certificate_file(ctx, cert, 1); |
| 4100 | | SSL_CTX_use_PrivateKey_file(ctx, cert, 1); |
| 4101 | | SSL_CTX_use_certificate_chain_file(ctx, cert); |
| 4102 | | |
| 4103 | | // When clear-text reply is pushed to client, switch to SSL mode. |
| 4104 | | n = send(conn->ns_conn->sock, conn->ns_conn->send_iobuf.buf, |
| 4105 | | conn->ns_conn->send_iobuf.len, 0); |
| 4106 | | DBG(("%p %lu %d SEND", c, conn->ns_conn->send_iobuf.len, n)); |
| 4107 | | conn->ns_conn->send_iobuf.len = 0; |
| 4108 | | if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) { |
| 4109 | | //SSL_set_fd((SSL *) c->connection_param, conn->ns_conn->sock); |
| 4110 | | SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock); |
| 4111 | | } |
| 4112 | | SSL_CTX_free(ctx); |
| 4113 | | } |
| 4114 | | } |
| 4115 | | #endif |
| 4116 | 4114 | } else { |
| 4117 | 4115 | // For other methods, forward the request to the target host. |
| 4118 | 4116 | c->uri += n; |
| r30951 | r30952 | |
| 4187 | 4185 | #endif // !MONGOOSE_NO_FILESYSTEM |
| 4188 | 4186 | |
| 4189 | 4187 | static void open_local_endpoint(struct connection *conn, int skip_user) { |
| 4188 | #ifndef MONGOOSE_NO_FILESYSTEM |
| 4190 | 4189 | char path[MAX_PATH_SIZE]; |
| 4191 | 4190 | file_stat_t st; |
| 4192 | 4191 | int exists = 0; |
| 4192 | #endif |
| 4193 | 4193 | |
| 4194 | 4194 | // If EP_USER was set in a prev call, reset it |
| 4195 | 4195 | conn->endpoint_type = EP_NONE; |
| r30951 | r30952 | |
| 4302 | 4302 | |
| 4303 | 4303 | static void do_proxy(struct connection *conn) { |
| 4304 | 4304 | if (conn->request_len == 0) { |
| 4305 | | DBG(("%p parsing", conn)); |
| 4306 | 4305 | try_parse(conn); |
| 4307 | | if (conn->request_len > 0 && |
| 4308 | | call_user(conn, MG_REQUEST) == MG_FALSE) { |
| 4306 | DBG(("%p parsing -> %d", conn, conn->request_len)); |
| 4307 | if (conn->request_len > 0 && call_user(conn, MG_REQUEST) == MG_FALSE) { |
| 4309 | 4308 | proxy_request(conn->endpoint.nc, &conn->mg_conn); |
| 4310 | 4309 | } else if (conn->request_len < 0) { |
| 4311 | 4310 | ns_forward(conn->ns_conn, conn->endpoint.nc); |
| r30951 | r30952 | |
| 4564 | 4563 | } |
| 4565 | 4564 | } |
| 4566 | 4565 | |
| 4566 | struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) { |
| 4567 | struct connection *conn = MG_CONN_2_CONN(c); |
| 4568 | struct ns_connection *nc = ns_next(&s->ns_server, |
| 4569 | c == NULL ? NULL : conn->ns_conn); |
| 4570 | |
| 4571 | return nc == NULL ? NULL : |
| 4572 | & ((struct connection *) nc->connection_data)->mg_conn; |
| 4573 | } |
| 4574 | |
| 4567 | 4575 | // Apply function to all active connections. |
| 4568 | 4576 | void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb, |
| 4569 | 4577 | void *param) { |
| r30951 | r30952 | |
| 4730 | 4738 | if (port < 0) { |
| 4731 | 4739 | error_msg = "Cannot bind to port"; |
| 4732 | 4740 | } else { |
| 4733 | | if (!strcmp(value, "0")) { |
| 4734 | | char buf[10]; |
| 4735 | | mg_snprintf(buf, sizeof(buf), "%d", port); |
| 4736 | | free(*v); |
| 4737 | | *v = mg_strdup(buf); |
| 4738 | | } |
| 4741 | char buf[100]; |
| 4742 | ns_sock_to_str(server->ns_server.listening_sock, buf, sizeof(buf), 2); |
| 4743 | free(*v); |
| 4744 | *v = mg_strdup(buf); |
| 4739 | 4745 | } |
| 4740 | 4746 | #ifndef _WIN32 |
| 4741 | 4747 | } else if (ind == RUN_AS_USER) { |
| r30951 | r30952 | |
| 4829 | 4835 | |
| 4830 | 4836 | static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { |
| 4831 | 4837 | struct connection *conn = (struct connection *) nc->connection_data; |
| 4838 | #ifndef MONGOOSE_NO_FILESYSTEM |
| 4832 | 4839 | struct mg_server *server = (struct mg_server *) nc->server; |
| 4840 | #endif |
| 4833 | 4841 | |
| 4834 | 4842 | // Send NS event to the handler. Note that call_user won't send an event |
| 4835 | 4843 | // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well. |