Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_altcp_proxyconnect.c Source File

lwip_altcp_proxyconnect.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * Application layered TCP connection API that executes a proxy-connect.
00004  *
00005  * This file provides a starting layer that executes a proxy-connect e.g. to
00006  * set up TLS connections through a http proxy.
00007  */
00008 
00009 /*
00010  * Copyright (c) 2018 Simon Goldschmidt
00011  * All rights reserved.
00012  *
00013  * Redistribution and use in source and binary forms, with or without modification,
00014  * are permitted provided that the following conditions are met:
00015  *
00016  * 1. Redistributions of source code must retain the above copyright notice,
00017  *    this list of conditions and the following disclaimer.
00018  * 2. Redistributions in binary form must reproduce the above copyright notice,
00019  *    this list of conditions and the following disclaimer in the documentation
00020  *    and/or other materials provided with the distribution.
00021  * 3. The name of the author may not be used to endorse or promote products
00022  *    derived from this software without specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00025  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00026  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00027  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00028  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00029  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00032  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00033  * OF SUCH DAMAGE.
00034  *
00035  * This file is part of the lwIP TCP/IP stack.
00036  *
00037  * Author: Simon Goldschmidt <goldsimon@gmx.de>
00038  *
00039  */
00040 
00041 #include "lwip/apps/altcp_proxyconnect.h"
00042 
00043 #if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
00044 
00045 #include "lwip/altcp.h"
00046 #include "lwip/priv/altcp_priv.h"
00047 
00048 #include "lwip/altcp_tcp.h"
00049 #include "lwip/altcp_tls.h"
00050 
00051 #include "lwip/mem.h"
00052 #include "lwip/init.h"
00053 
00054 #include <stdio.h>
00055 
00056 /** This string is passed in the HTTP header as "User-Agent: " */
00057 #ifndef ALTCP_PROXYCONNECT_CLIENT_AGENT
00058 #define ALTCP_PROXYCONNECT_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
00059 #endif
00060 
00061 #define ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED  0x01
00062 #define ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE   0x02
00063 
00064 typedef struct altcp_proxyconnect_state_s
00065 {
00066   ip_addr_t outer_addr;
00067   u16_t outer_port;
00068   struct altcp_proxyconnect_config *conf;
00069   u8_t flags;
00070 } altcp_proxyconnect_state_t;
00071 
00072 /* Variable prototype, the actual declaration is at the end of this file
00073    since it contains pointers to static functions declared here */
00074 extern const struct altcp_functions altcp_proxyconnect_functions;
00075 
00076 /* memory management functions: */
00077 
00078 static altcp_proxyconnect_state_t *
00079 altcp_proxyconnect_state_alloc(void)
00080 {
00081   altcp_proxyconnect_state_t *ret = (altcp_proxyconnect_state_t *)mem_calloc(1, sizeof(altcp_proxyconnect_state_t));
00082   return ret;
00083 }
00084 
00085 static void
00086 altcp_proxyconnect_state_free(altcp_proxyconnect_state_t *state)
00087 {
00088   LWIP_ASSERT("state != NULL", state != NULL);
00089   mem_free(state);
00090 }
00091 
00092 /* helper functions */
00093 
00094 #define PROXY_CONNECT "CONNECT %s:%d HTTP/1.1\r\n" /* HOST, PORT */ \
00095   "User-Agent: %s\r\n" /* User-Agent */\
00096   "Proxy-Connection: keep-alive\r\n" \
00097   "Connection: keep-alive\r\n" \
00098   "\r\n"
00099 #define PROXY_CONNECT_FORMAT(host, port) PROXY_CONNECT, host, port, ALTCP_PROXYCONNECT_CLIENT_AGENT
00100 
00101 /* Format the http proxy connect request via snprintf */
00102 static int
00103 altcp_proxyconnect_format_request(char *buffer, size_t bufsize, const char *host, int port)
00104 {
00105   return snprintf(buffer, bufsize, PROXY_CONNECT_FORMAT(host, port));
00106 }
00107 
00108 /* Create and send the http proxy connect request */
00109 static err_t
00110 altcp_proxyconnect_send_request(struct altcp_pcb *conn)
00111 {
00112   int len, len2;
00113   mem_size_t alloc_len;
00114   char *buffer, *host;
00115   altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
00116 
00117   if (!state) {
00118     return ERR_VAL;
00119   }
00120   /* Use printf with zero length to get the required allocation size */
00121   len = altcp_proxyconnect_format_request(NULL, 0, "", state->outer_port);
00122   if (len < 0) {
00123     return ERR_VAL;
00124   }
00125   /* add allocation size for IP address strings */
00126 #if LWIP_IPV6
00127   len += 40; /* worst-case IPv6 address length */
00128 #else
00129   len += 16; /* worst-case IPv4 address length */
00130 #endif
00131   alloc_len = (mem_size_t)len;
00132   if ((len < 0) || (int)alloc_len != len) {
00133     /* overflow */
00134     return ERR_MEM;
00135   }
00136   /* Allocate a bufer for the request string */
00137   buffer = (char *)mem_malloc(alloc_len);
00138   if (buffer == NULL) {
00139     return ERR_MEM;
00140   }
00141   host = ipaddr_ntoa(&state->outer_addr);
00142   len2 = altcp_proxyconnect_format_request(buffer, alloc_len, host, state->outer_port);
00143   if ((len2 > 0) && (len2 <= len) && (len2 <= 0xFFFF)) {
00144     err_t err = altcp_write (conn->inner_conn, buffer, (u16_t)len2, TCP_WRITE_FLAG_COPY);
00145     if (err != ERR_OK) {
00146       /* @todo: abort? */
00147       mem_free(buffer);
00148       return err;
00149     }
00150   }
00151   mem_free(buffer);
00152   return ERR_OK;
00153 }
00154 
00155 /* callback functions from inner/lower connection: */
00156 
00157 /** Connected callback from lower connection (i.e. TCP).
00158  * Not really implemented/tested yet...
00159  */
00160 static err_t
00161 altcp_proxyconnect_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
00162 {
00163   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
00164   if (conn && conn->state) {
00165     LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
00166     LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
00167     /* upper connected is called when handshake is done */
00168     if (err != ERR_OK) {
00169       if (conn->connected) {
00170         if (conn->connected(conn->arg, conn, err) == ERR_ABRT) {
00171           return ERR_ABRT;
00172         }
00173         return ERR_OK;
00174       }
00175     }
00176     /* send proxy connect request here */
00177     return altcp_proxyconnect_send_request(conn);
00178   }
00179   return ERR_VAL;
00180 }
00181 
00182 /** Recv callback from lower connection (i.e. TCP)
00183  * This one mainly differs between connection setup (wait for proxy OK string)
00184  * and application phase (data is passed on to the application).
00185  */
00186 static err_t
00187 altcp_proxyconnect_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
00188 {
00189   altcp_proxyconnect_state_t *state;
00190   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
00191 
00192   LWIP_ASSERT("no err expected", err == ERR_OK);
00193   LWIP_UNUSED_ARG(err);
00194 
00195   if (!conn) {
00196     /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
00197     if (p != NULL) {
00198       pbuf_free(p);
00199     }
00200     altcp_close (inner_conn);
00201     return ERR_CLSD;
00202   }
00203   state = (altcp_proxyconnect_state_t *)conn->state;
00204   LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
00205   if (!state) {
00206     /* already closed */
00207     if (p != NULL) {
00208       pbuf_free(p);
00209     }
00210     altcp_close (inner_conn);
00211     return ERR_CLSD;
00212   }
00213   if (state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE) {
00214     /* application phase, just pass this through */
00215     if (conn->recv) {
00216       return conn->recv(conn->arg, conn, p, err);
00217     }
00218     pbuf_free(p);
00219     return ERR_OK;
00220   } else {
00221     /* setup phase */
00222     /* handle NULL pbuf (inner connection closed) */
00223     if (p == NULL) {
00224       if (altcp_close (conn) != ERR_OK) {
00225         altcp_abort (conn);
00226         return ERR_ABRT;
00227       }
00228       return ERR_OK;
00229     } else {
00230       /* @todo: parse setup phase rx data
00231          for now, we just wait for the end of the header... */
00232       u16_t idx = pbuf_memfind(p, "\r\n\r\n", 4, 0);
00233       altcp_recved (inner_conn, p->tot_len);
00234       pbuf_free(p);
00235       if (idx != 0xFFFF) {
00236         state->flags |= ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE;
00237         if (conn->connected) {
00238           return conn->connected(conn->arg, conn, ERR_OK);
00239         }
00240       }
00241       return ERR_OK;
00242     }
00243   }
00244 }
00245 
00246 /** Sent callback from lower connection (i.e. TCP)
00247  * This only informs the upper layer to try to send more, not about
00248  * the number of ACKed bytes.
00249  */
00250 static err_t
00251 altcp_proxyconnect_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
00252 {
00253   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
00254   LWIP_UNUSED_ARG(len);
00255   if (conn) {
00256     altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
00257     LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
00258     LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
00259     if (!state || !(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
00260       /* @todo: do something here? */
00261       return ERR_OK;
00262     }
00263     /* pass this on to upper sent */
00264     if (conn->sent) {
00265       return conn->sent(conn->arg, conn, len);
00266     }
00267   }
00268   return ERR_OK;
00269 }
00270 
00271 /** Poll callback from lower connection (i.e. TCP)
00272  * Just pass this on to the application.
00273  * @todo: retry sending?
00274  */
00275 static err_t
00276 altcp_proxyconnect_lower_poll(void *arg, struct altcp_pcb *inner_conn)
00277 {
00278   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
00279   if (conn) {
00280     LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
00281     LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
00282     if (conn->poll) {
00283       return conn->poll(conn->arg, conn);
00284     }
00285   }
00286   return ERR_OK;
00287 }
00288 
00289 static void
00290 altcp_proxyconnect_lower_err(void *arg, err_t err)
00291 {
00292   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
00293   if (conn) {
00294     conn->inner_conn = NULL; /* already freed */
00295     if (conn->err) {
00296       conn->err(conn->arg, err);
00297     }
00298     altcp_free(conn);
00299   }
00300 }
00301 
00302 
00303 /* setup functions */
00304 
00305 static void
00306 altcp_proxyconnect_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
00307 {
00308   altcp_arg (inner_conn, conn);
00309   altcp_recv (inner_conn, altcp_proxyconnect_lower_recv);
00310   altcp_sent (inner_conn, altcp_proxyconnect_lower_sent);
00311   altcp_err (inner_conn, altcp_proxyconnect_lower_err);
00312   /* tcp_poll is set when interval is set by application */
00313   /* listen is set totally different :-) */
00314 }
00315 
00316 static err_t
00317 altcp_proxyconnect_setup(struct altcp_proxyconnect_config *config, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
00318 {
00319   altcp_proxyconnect_state_t *state;
00320   if (!config) {
00321     return ERR_ARG;
00322   }
00323   LWIP_ASSERT("invalid inner_conn", conn != inner_conn);
00324 
00325   /* allocate proxyconnect context */
00326   state = altcp_proxyconnect_state_alloc();
00327   if (state == NULL) {
00328     return ERR_MEM;
00329   }
00330   state->flags = 0;
00331   state->conf = config;
00332   altcp_proxyconnect_setup_callbacks(conn, inner_conn);
00333   conn->inner_conn = inner_conn;
00334   conn->fns = &altcp_proxyconnect_functions;
00335   conn->state = state;
00336   return ERR_OK;
00337 }
00338 
00339 /** Allocate a new altcp layer connecting through a proxy.
00340  * This function gets the inner pcb passed.
00341  *
00342  * @param config struct altcp_proxyconnect_config that contains the proxy settings
00343  * @param inner_pcb pcb that makes the connection to the proxy (i.e. tcp pcb)
00344  */
00345 struct altcp_pcb *
00346 altcp_proxyconnect_new(struct altcp_proxyconnect_config *config, struct altcp_pcb *inner_pcb)
00347 {
00348   struct altcp_pcb *ret;
00349   if (inner_pcb == NULL) {
00350     return NULL;
00351   }
00352   ret = altcp_alloc();
00353   if (ret != NULL) {
00354     if (altcp_proxyconnect_setup(config, ret, inner_pcb) != ERR_OK) {
00355       altcp_free(ret);
00356       return NULL;
00357     }
00358   }
00359   return ret;
00360 }
00361 
00362 /** Allocate a new altcp layer connecting through a proxy.
00363  * This function allocates the inner pcb as tcp pcb, resulting in a direct tcp
00364  * connection to the proxy.
00365  *
00366  * @param config struct altcp_proxyconnect_config that contains the proxy settings
00367  * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
00368  */
00369 struct altcp_pcb *
00370 altcp_proxyconnect_new_tcp(struct altcp_proxyconnect_config *config, u8_t ip_type)
00371 {
00372   struct altcp_pcb *inner_pcb, *ret;
00373 
00374   /* inner pcb is tcp */
00375   inner_pcb = altcp_tcp_new_ip_type(ip_type);
00376   if (inner_pcb == NULL) {
00377     return NULL;
00378   }
00379   ret = altcp_proxyconnect_new(config, inner_pcb);
00380   if (ret == NULL) {
00381     altcp_close (inner_pcb);
00382   }
00383   return ret;
00384 }
00385 
00386 /** Allocator function to allocate a proxy connect altcp pcb connecting directly
00387  * via tcp to the proxy.
00388  *
00389  * The returned pcb is a chain: altcp_proxyconnect - altcp_tcp - tcp pcb
00390  *
00391  * This function is meant for use with @ref altcp_new.
00392  *
00393  * @param arg struct altcp_proxyconnect_config that contains the proxy settings
00394  * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
00395  */
00396 struct altcp_pcb *
00397 altcp_proxyconnect_alloc(void *arg, u8_t ip_type)
00398 {
00399   return altcp_proxyconnect_new_tcp((struct altcp_proxyconnect_config *)arg, ip_type);
00400 }
00401 
00402 
00403 #if LWIP_ALTCP_TLS
00404 
00405 /** Allocator function to allocate a TLS connection through a proxy.
00406  *
00407  * The returned pcb is a chain: altcp_tls - altcp_proxyconnect - altcp_tcp - tcp pcb
00408  *
00409  * This function is meant for use with @ref altcp_new.
00410  *
00411  * @param arg struct altcp_proxyconnect_tls_config that contains the proxy settings
00412  *        and tls settings
00413  * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
00414  */
00415 struct altcp_pcb *
00416 altcp_proxyconnect_tls_alloc(void *arg, u8_t ip_type)
00417 {
00418   struct altcp_proxyconnect_tls_config *cfg = (struct altcp_proxyconnect_tls_config *)arg;
00419   struct altcp_pcb *proxy_pcb;
00420   struct altcp_pcb *tls_pcb;
00421 
00422   proxy_pcb = altcp_proxyconnect_new_tcp(&cfg->proxy, ip_type);
00423   tls_pcb = altcp_tls_wrap(cfg->tls_config, proxy_pcb);
00424 
00425   if (tls_pcb == NULL) {
00426     altcp_close (proxy_pcb);
00427   }
00428   return tls_pcb;
00429 }
00430 #endif /* LWIP_ALTCP_TLS */
00431 
00432 /* "virtual" functions */
00433 static void
00434 altcp_proxyconnect_set_poll(struct altcp_pcb *conn, u8_t interval)
00435 {
00436   if (conn != NULL) {
00437     altcp_poll (conn->inner_conn, altcp_proxyconnect_lower_poll, interval);
00438   }
00439 }
00440 
00441 static void
00442 altcp_proxyconnect_recved(struct altcp_pcb *conn, u16_t len)
00443 {
00444   altcp_proxyconnect_state_t *state;
00445   if (conn == NULL) {
00446     return;
00447   }
00448   state = (altcp_proxyconnect_state_t *)conn->state;
00449   if (state == NULL) {
00450     return;
00451   }
00452   if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
00453     return;
00454   }
00455   altcp_recved (conn->inner_conn, len);
00456 }
00457 
00458 static err_t
00459 altcp_proxyconnect_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
00460 {
00461   altcp_proxyconnect_state_t *state;
00462 
00463   if ((conn == NULL) || (ipaddr == NULL)) {
00464     return ERR_VAL;
00465   }
00466   state = (altcp_proxyconnect_state_t *)conn->state;
00467   if (state == NULL) {
00468     return ERR_VAL;
00469   }
00470   if (state->flags & ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED) {
00471     return ERR_VAL;
00472   }
00473   state->flags |= ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED;
00474 
00475   conn->connected = connected;
00476   /* connect to our proxy instead, but store the requested address and port */
00477   ip_addr_copy(state->outer_addr, *ipaddr);
00478   state->outer_port = port;
00479 
00480   return altcp_connect (conn->inner_conn, &state->conf->proxy_addr, state->conf->proxy_port, altcp_proxyconnect_lower_connected);
00481 }
00482 
00483 static struct altcp_pcb *
00484 altcp_proxyconnect_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
00485 {
00486   LWIP_UNUSED_ARG(conn);
00487   LWIP_UNUSED_ARG(backlog);
00488   LWIP_UNUSED_ARG(err);
00489   /* listen not supported! */
00490   return NULL;
00491 }
00492 
00493 static void
00494 altcp_proxyconnect_abort(struct altcp_pcb *conn)
00495 {
00496   if (conn != NULL) {
00497     if (conn->inner_conn != NULL) {
00498       altcp_abort (conn->inner_conn);
00499     }
00500     altcp_free(conn);
00501   }
00502 }
00503 
00504 static err_t
00505 altcp_proxyconnect_close(struct altcp_pcb *conn)
00506 {
00507   if (conn == NULL) {
00508     return ERR_VAL;
00509   }
00510   if (conn->inner_conn != NULL) {
00511     err_t err = altcp_close (conn->inner_conn);
00512     if (err != ERR_OK) {
00513       /* closing inner conn failed, return the error */
00514       return err;
00515     }
00516   }
00517   /* no inner conn or closing it succeeded, deallocate myself */
00518   altcp_free(conn);
00519   return ERR_OK;
00520 }
00521 
00522 static err_t
00523 altcp_proxyconnect_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
00524 {
00525   altcp_proxyconnect_state_t *state;
00526 
00527   LWIP_UNUSED_ARG(apiflags);
00528 
00529   if (conn == NULL) {
00530     return ERR_VAL;
00531   }
00532 
00533   state = (altcp_proxyconnect_state_t *)conn->state;
00534   if (state == NULL) {
00535     /* @todo: which error? */
00536     return ERR_CLSD;
00537   }
00538   if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
00539     /* @todo: which error? */
00540     return ERR_VAL;
00541   }
00542   return altcp_write (conn->inner_conn, dataptr, len, apiflags);
00543 }
00544 
00545 static void
00546 altcp_proxyconnect_dealloc(struct altcp_pcb *conn)
00547 {
00548   /* clean up and free tls state */
00549   if (conn) {
00550     altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
00551     if (state) {
00552       altcp_proxyconnect_state_free(state);
00553       conn->state = NULL;
00554     }
00555   }
00556 }
00557 const struct altcp_functions altcp_proxyconnect_functions = {
00558   altcp_proxyconnect_set_poll,
00559   altcp_proxyconnect_recved,
00560   altcp_default_bind,
00561   altcp_proxyconnect_connect,
00562   altcp_proxyconnect_listen,
00563   altcp_proxyconnect_abort,
00564   altcp_proxyconnect_close,
00565   altcp_default_shutdown,
00566   altcp_proxyconnect_write,
00567   altcp_default_output,
00568   altcp_default_mss,
00569   altcp_default_sndbuf,
00570   altcp_default_sndqueuelen,
00571   altcp_default_nagle_disable,
00572   altcp_default_nagle_enable,
00573   altcp_default_nagle_disabled,
00574   altcp_default_setprio,
00575   altcp_proxyconnect_dealloc,
00576   altcp_default_get_tcp_addrinfo,
00577   altcp_default_get_ip,
00578   altcp_default_get_port
00579 #ifdef LWIP_DEBUG
00580   , altcp_default_dbg_get_tcp_state
00581 #endif
00582 };
00583 
00584 #endif /* LWIP_ALTCP */