|
4 | 4 | #define MG_ENABLE_PACKED_FS 0 |
5 | 5 |
|
6 | 6 | #include <assert.h> |
| 7 | +#include <linux/if.h> |
| 8 | +#include <linux/if_tun.h> |
| 9 | +#include <sys/ioctl.h> |
7 | 10 | #include "mongoose.c" |
8 | 11 |
|
9 | | -#include "driver_mock.c" |
| 12 | + #include "driver_mock.c" |
| 13 | + |
| 14 | +static int s_num_tests = 0; |
| 15 | + |
| 16 | +#define ASSERT(expr) \ |
| 17 | + do { \ |
| 18 | + s_num_tests++; \ |
| 19 | + if (!(expr)) { \ |
| 20 | + printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \ |
| 21 | + abort(); \ |
| 22 | + } \ |
| 23 | + } while (0) |
| 24 | + |
| 25 | + |
10 | 26 |
|
11 | 27 | static void test_queue(void) { |
12 | 28 | static uint8_t |
@@ -49,9 +65,191 @@ static void test_statechange(void) { |
49 | 65 | onstatechange(&iface); |
50 | 66 | } |
51 | 67 |
|
| 68 | +// MIP TUNTAP driver |
| 69 | +static size_t tap_rx(void *buf, size_t len, void *userdata) { |
| 70 | + ssize_t received = read(*(int *) userdata, buf, len); |
| 71 | + usleep(1); // This is to avoid 100% CPU |
| 72 | + if (received < 0) return 0; |
| 73 | + return (size_t) received; |
| 74 | +} |
| 75 | + |
| 76 | +static size_t tap_tx(const void *buf, size_t len, void *userdata) { |
| 77 | + ssize_t res = write(*(int *) userdata, buf, len); |
| 78 | + if (res < 0) { |
| 79 | + MG_ERROR(("tap_tx failed: %d", errno)); |
| 80 | + return 0; |
| 81 | + } |
| 82 | + return (size_t) res; |
| 83 | +} |
| 84 | + |
| 85 | +static bool tap_up(void *userdata) { |
| 86 | + return userdata ? true : false; |
| 87 | +} |
| 88 | + |
| 89 | +// HTTP fetches IOs |
| 90 | +struct Post_reply { |
| 91 | + char* post; // HTTP POST data |
| 92 | + void* http_response; // Server response(s) |
| 93 | + unsigned int http_responses_received; // Number responses received |
| 94 | +}; |
| 95 | + |
| 96 | +char *fetch(struct mg_mgr *mgr, const char *url, const char *post_data); |
| 97 | +static void f_http_fetch_query(struct mg_connection *c, int ev, void *ev_data, void *fn_data); |
| 98 | +int get_response_code(char *); // Returns HTTP status code from full char* msg |
| 99 | + |
| 100 | +static void f_http_fetch_query(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { |
| 101 | + static char* http_response = 0; |
| 102 | + static bool http_response_allocated = 0; // So that we will update out parameter |
| 103 | + unsigned int http_responses_received = 0; |
| 104 | + struct Post_reply *post_reply_l; |
| 105 | + post_reply_l = (struct Post_reply*)fn_data; |
| 106 | + |
| 107 | + if (ev == MG_EV_CONNECT) { |
| 108 | + mg_printf(c, post_reply_l->post); |
| 109 | + } else if (ev == MG_EV_HTTP_MSG) { |
| 110 | + struct mg_http_message *hm = (struct mg_http_message *) ev_data; |
| 111 | + http_responses_received++; |
| 112 | + if (!http_response_allocated) { |
| 113 | + http_response = (char*)mg_strdup(hm->message).ptr; |
| 114 | + http_response_allocated = 1; |
| 115 | + } |
| 116 | + if (http_responses_received > 0) { |
| 117 | + post_reply_l->http_response = http_response; |
| 118 | + post_reply_l->http_responses_received = http_responses_received; |
| 119 | + } |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +// Fetch utility returns message from fetch(..., URL, POST) |
| 124 | +char *fetch(struct mg_mgr *mgr, const char *url, const char *fn_data) { |
| 125 | + struct Post_reply post_reply; |
| 126 | + { |
| 127 | + post_reply.post=(char*)fn_data; |
| 128 | + post_reply.http_response=0; |
| 129 | + post_reply.http_responses_received=0; |
| 130 | + } |
| 131 | + struct mg_connection *conn; |
| 132 | + conn = mg_http_connect(mgr, url, f_http_fetch_query, &post_reply); |
| 133 | + ASSERT(conn != NULL); // Assertion on initialisation |
| 134 | + for (int i = 0; i < 500 && !post_reply.http_responses_received; i++) { |
| 135 | + mg_mgr_poll(mgr, 100); |
| 136 | + usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit |
| 137 | + } |
| 138 | + conn->is_closing = 1; |
| 139 | + mg_mgr_poll(mgr, 0); |
| 140 | + if (!post_reply.http_responses_received) |
| 141 | + return 0; |
| 142 | + else |
| 143 | + return (char*)post_reply.http_response; |
| 144 | +} |
| 145 | + |
| 146 | +// Returns server's HTTP response code |
| 147 | +int get_response_code(char * http_msg_raw) { |
| 148 | + int http_status = 0; |
| 149 | + struct mg_http_message http_msg_parsed; |
| 150 | + if (mg_http_parse(http_msg_raw, strlen(http_msg_raw), &http_msg_parsed)) { |
| 151 | + http_status = mg_http_status(&http_msg_parsed); |
| 152 | + } else { |
| 153 | + printf("Error: mg_http_parse()\n"); |
| 154 | + ASSERT(http_status != 0); // Couldn't parse. |
| 155 | + } |
| 156 | + return http_status; |
| 157 | +} |
| 158 | + |
| 159 | +static void test_http_fetch(void) { |
| 160 | + // Setup interface |
| 161 | + const char *iface = "tap0"; // Network iface |
| 162 | + const char *mac = "00:00:01:02:03:78"; // MAC address |
| 163 | + int fd = open("/dev/net/tun", O_RDWR); // Open network interface |
| 164 | + |
| 165 | + struct ifreq ifr; |
| 166 | + memset(&ifr, 0, sizeof(ifr)); |
| 167 | + strncpy(ifr.ifr_name, iface, IFNAMSIZ); |
| 168 | + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; |
| 169 | + if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) { |
| 170 | + MG_ERROR(("Failed to setup TAP interface: %s", ifr.ifr_name)); |
| 171 | + abort(); // return EXIT_FAILURE; |
| 172 | + } |
| 173 | + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode |
| 174 | + |
| 175 | + MG_INFO(("Opened TAP interface: %s", iface)); |
| 176 | + |
| 177 | + // Events |
| 178 | + struct mg_mgr mgr; // Event manager |
| 179 | + mg_mgr_init(&mgr); // Initialise event manager |
| 180 | + |
| 181 | + // MIP driver |
| 182 | + |
| 183 | + // Zero init fields required (C/C++ style diverge) |
| 184 | + #ifndef __cplusplus |
| 185 | + struct mip_driver driver = {.tx = tap_tx, .up = tap_up, .rx = tap_rx}; |
| 186 | + struct mip_if mif = {.use_dhcp = true, .driver = &driver, .driver_data = &fd}; |
| 187 | + #else |
| 188 | + struct mip_driver driver {}; |
| 189 | + driver.tx = tap_tx; |
| 190 | + driver.up = tap_up; |
| 191 | + driver.rx = tap_rx; |
| 192 | + struct mip_if mif {}; |
| 193 | + mif.use_dhcp = true; |
| 194 | + mif.driver = &driver; |
| 195 | + mif.driver_data = &fd; |
| 196 | + #endif |
| 197 | + |
| 198 | + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], &mif.mac[2], |
| 199 | + &mif.mac[3], &mif.mac[4], &mif.mac[5]); |
| 200 | + |
| 201 | + mip_init(&mgr, &mif); |
| 202 | + MG_INFO(("Init done, starting main loop")); |
| 203 | + |
| 204 | + // DHCP lease |
| 205 | + { |
| 206 | + if (mif.ip) printf("MIF configuration error: not configured for DHCP\n"); |
| 207 | + ASSERT(!mif.ip); // Check we are set for DHCP |
| 208 | + int pc = 500; // Timout on DHCP lease 500 ~ approx 5s (typical delay <1s) |
| 209 | + while (((pc--)>0)/* & !mif.ip*/) { |
| 210 | + mg_mgr_poll(&mgr, 100); |
| 211 | + usleep(10000); // 10 ms |
| 212 | + } |
| 213 | + if (!mif.ip) printf("DHCP lease failed.\n"); |
| 214 | + ASSERT(mif.ip); // We have a received lease |
| 215 | + } |
| 216 | + |
| 217 | + // Simple HTTP fetch |
| 218 | + { |
| 219 | + char* http_feedback = (char*)""; |
| 220 | + const bool ipv6 = 0; |
| 221 | + if (ipv6) { |
| 222 | + http_feedback = fetch (&mgr, "ipv6.google.com",\ |
| 223 | + "GET/ HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n"); |
| 224 | + } else { |
| 225 | + http_feedback = fetch (&mgr, "http://cesanta.com",\ |
| 226 | + "GET //robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n"); |
| 227 | + } |
| 228 | + |
| 229 | + ASSERT(*http_feedback != '\0'); // Received HTTP response ? |
| 230 | + |
| 231 | + int http_status = get_response_code(http_feedback); |
| 232 | + // printf("Server response HTTP status code: %d\n",http_status); |
| 233 | + ASSERT(http_status != 0); |
| 234 | + ASSERT(http_status == 301); // OK: Permanently moved (HTTP->HTTPS redirect) |
| 235 | + |
| 236 | + if (http_feedback) { |
| 237 | + free(http_feedback); |
| 238 | + http_feedback = 0; |
| 239 | + } |
| 240 | + } |
| 241 | + |
| 242 | + // Clear |
| 243 | + mip_free(&mif); |
| 244 | + mg_mgr_free(&mgr); |
| 245 | + ASSERT(mgr.conns == NULL); // Deconstruction OK |
| 246 | + close(fd); |
| 247 | +} |
| 248 | + |
52 | 249 | int main(void) { |
53 | 250 | test_queue(); |
54 | 251 | test_statechange(); |
| 252 | + test_http_fetch(); |
55 | 253 | printf("SUCCESS\n"); |
56 | 254 | return 0; |
57 | 255 | } |
0 commit comments