pan_lwip_http_server.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. * Copyright (C) 2014 BlueKitchen GmbH
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. * 4. Any redistribution, use, or modification is done solely for
  17. * personal benefit and not for any commercial purpose or for
  18. * monetary gain.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
  21. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
  24. * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * Please inquire about commercial licensing options at
  34. * contact@bluekitchen-gmbh.com
  35. *
  36. */
  37. #define __BTSTACK_FILE__ "pan_lwip_http_server.c"
  38. // *****************************************************************************
  39. /* EXAMPLE_START(pan_lwip_http_server): PAN - HTTP Server using lwIP
  40. *
  41. * @text Bluetooth PAN is mainly used for Internet Tethering, where e.g. a mobile
  42. * phone provides internet connection to a laptop or a tablet.
  43. *
  44. * Instead of regular internet access, it's also possible to provide a Web app on a
  45. * Bluetooth device, e.g. for configuration or maintenance. For some device,
  46. * this can be a more effective way to provide an interface compared to dedicated
  47. * smartphone applications (for Android and iOS).
  48. *
  49. * Before iOS 11, accessing an HTTP server via Bluetooth PAN was not supported on
  50. * the iPhone, but on iPod and iPad. With iOS 11, this works as expected.
  51. *
  52. * After pairing your device, please open the URL http://192.168.7.1 in your web
  53. * browser.
  54. */
  55. // *****************************************************************************
  56. #include <stdio.h>
  57. #include <inttypes.h>
  58. #include "btstack_config.h"
  59. #include "bnep_lwip.h"
  60. #include "btstack.h"
  61. #include "lwip/init.h"
  62. #include "lwip/opt.h"
  63. #include "lwip/tcpip.h"
  64. #include "lwip/apps/fs.h"
  65. #include "lwip/apps/httpd.h"
  66. #include "dhserver.h"
  67. #if defined(LWIP_HTTPD_CUSTOM_FILES) && defined(LWIP_HTTPD_DYNAMIC_HEADERS)
  68. #define ENABLE_TEST_FILE_DOWNLOAD
  69. #else
  70. #warning "Test file download requires LWIP_HTTPD_CUSTOM_FILES and LWIP_HTTPD_DYNAMIC_HEADERS to be defined. Not supported currently."
  71. #endif
  72. // network types
  73. #define NETWORK_TYPE_IPv4 0x0800
  74. #define NETWORK_TYPE_ARP 0x0806
  75. #define NETWORK_TYPE_IPv6 0x86DD
  76. static uint8_t pan_sdp_record[220];
  77. static btstack_packet_callback_registration_t hci_event_callback_registration;
  78. /*
  79. * @section Packet Handler
  80. *
  81. * @text All BNEP events are handled in the platform/bnep_lwip.c BNEP-LWIP Adapter.
  82. * Here, we only print status information and handle pairing requests.
  83. */
  84. /* LISTING_START(packetHandler): Packet Handler */
  85. static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  86. /* LISTING_PAUSE */
  87. UNUSED(channel);
  88. UNUSED(size);
  89. bd_addr_t event_addr;
  90. switch (packet_type) {
  91. case HCI_EVENT_PACKET:
  92. switch (hci_event_packet_get_type(packet)) {
  93. case HCI_EVENT_PIN_CODE_REQUEST:
  94. // inform about pin code request
  95. printf("Pin code request - using '0000'\n");
  96. hci_event_pin_code_request_get_bd_addr(packet, event_addr);
  97. gap_pin_code_response(event_addr, "0000");
  98. break;
  99. case HCI_EVENT_USER_CONFIRMATION_REQUEST:
  100. // inform about user confirmation request
  101. printf("SSP User Confirmation Auto accept\n");
  102. hci_event_user_confirmation_request_get_bd_addr(packet, event_addr);
  103. break;
  104. /* @text BNEP_EVENT_CHANNEL_OPENED is received after a BNEP connection was established or
  105. * or when the connection fails. The status field returns the error code.
  106. */
  107. case BNEP_EVENT_CHANNEL_OPENED:
  108. if (bnep_event_channel_opened_get_status(packet)) {
  109. printf("BNEP channel open failed, status %02x\n", bnep_event_channel_opened_get_status(packet));
  110. } else {
  111. uint16_t uuid_source = bnep_event_channel_opened_get_source_uuid(packet);
  112. uint16_t uuid_dest = bnep_event_channel_opened_get_destination_uuid(packet);
  113. uint16_t mtu = bnep_event_channel_opened_get_mtu(packet);
  114. bnep_event_channel_opened_get_remote_address(packet, event_addr);
  115. printf("BNEP connection open succeeded to %s source UUID 0x%04x dest UUID: 0x%04x, max frame size %u\n", bd_addr_to_str(event_addr), uuid_source, uuid_dest, mtu);
  116. printf("Please open 'http://192.168.7.1' in your web browser: \n");
  117. }
  118. break;
  119. /* @text BNEP_EVENT_CHANNEL_CLOSED is received when the connection gets closed.
  120. */
  121. case BNEP_EVENT_CHANNEL_CLOSED:
  122. printf("BNEP channel closed\n");
  123. break;
  124. default:
  125. break;
  126. }
  127. break;
  128. default:
  129. break;
  130. }
  131. }
  132. /* LISTING_END */
  133. /* @section PAN BNEP Setup
  134. *
  135. * @text Listing PanBnepSetup shows the setup of the PAN setup
  136. */
  137. /* LISTING_START(PanBnepSetup): Configure GAP, register PAN NAP servier, register PAN NA SDP record. */
  138. static void pan_bnep_setup(void){
  139. // Discoverable
  140. // Set local name with a template Bluetooth address, that will be automatically
  141. // replaced with a actual address once it is available, i.e. when BTstack boots
  142. // up and starts talking to a Bluetooth module.
  143. gap_set_local_name("PAN HTTP 00:00:00:00:00:00");
  144. gap_discoverable_control(1);
  145. // Major: Networking Device, Minor: Networ Access Point
  146. gap_set_class_of_device(0x20300);
  147. // register for HCI events
  148. hci_event_callback_registration.callback = &packet_handler;
  149. hci_add_event_handler(&hci_event_callback_registration);
  150. // Initialize L2CAP
  151. l2cap_init();
  152. // Initialize BNEP
  153. bnep_init();
  154. // Init SDP
  155. sdp_init();
  156. memset(pan_sdp_record, 0, sizeof(pan_sdp_record));
  157. uint16_t network_packet_types[] = { NETWORK_TYPE_IPv4, NETWORK_TYPE_ARP, 0}; // 0 as end of list
  158. // NAP Network Access Type: Other, 1 MB/s
  159. pan_create_nap_sdp_record(pan_sdp_record, sdp_create_service_record_handle(), network_packet_types, NULL, NULL, BNEP_SECURITY_NONE, PAN_NET_ACCESS_TYPE_OTHER, 1000000, NULL, NULL);
  160. sdp_register_service(pan_sdp_record);
  161. printf("SDP service record size: %u\n", de_get_len((uint8_t*) pan_sdp_record));
  162. // Init BNEP lwIP Adapter
  163. bnep_lwip_init();
  164. // Setup NAP Service via BENP lwIP adapter
  165. bnep_lwip_register_service(BLUETOOTH_SERVICE_CLASS_NAP, 1691);
  166. // register callback - to print state
  167. bnep_lwip_register_packet_handler(packet_handler);
  168. }
  169. /* LISTING_END */
  170. /* @section DHCP Server Configuration
  171. *
  172. * @text Listing DhcpSetup shows the DHCP Server configuration for network 192.168.7.0/8
  173. */
  174. /* LISTING_START(DhcpSetup): Setup DHCP Server with 3 entries for 192.168.7.0/8. */
  175. #define NUM_DHCP_ENTRY 3
  176. static dhcp_entry_t entries[NUM_DHCP_ENTRY] =
  177. {
  178. /* mac ip address subnet mask lease time */
  179. { {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 },
  180. { {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 },
  181. { {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 }
  182. };
  183. static dhcp_config_t dhcp_config =
  184. {
  185. {192, 168, 7, 1}, 67, /* server address, port */
  186. {0, 0, 0, 0}, /* dns server */
  187. NULL, /* dns suffix */
  188. NUM_DHCP_ENTRY, /* num entry */
  189. entries /* entries */
  190. };
  191. /* LISTING_END */
  192. #ifdef ENABLE_TEST_FILE_DOWNLOAD
  193. /* @section Large File Download
  194. *
  195. * @text Listing LargeFile Shows how a configurable test file for performance tests is generated on the fly.
  196. * The filename is the number of bytes to generate, e.g. /1048576.txt results in a 1MB file.
  197. */
  198. /* LISTING_START(LargeFile): Provide large file. */
  199. int fs_open_custom(struct fs_file *file, const char *name);
  200. int fs_open_custom(struct fs_file *file, const char *name){
  201. if (*name != '/') return 0;
  202. uint32_t file_size = btstack_atoi(&name[1]);
  203. if (file_size > 0) {
  204. printf("Serving '%s' with %u bytes\n", name, file_size);
  205. /* initialize fs_file correctly */
  206. memset(file, 0, sizeof(struct fs_file));
  207. file->len = file_size;
  208. file->index = 0;
  209. file->flags = FS_FILE_FLAGS_HEADER_PERSISTENT;
  210. return 1;
  211. }
  212. return 0;
  213. }
  214. static uint16_t cgi_buffer_fill(char * cgi_buffer, uint16_t cgi_buffer_size, uint32_t start_pos){
  215. // fill buffer with dots
  216. memset(cgi_buffer, (uint8_t) '.', cgi_buffer_size);
  217. uint16_t cursor = 10;
  218. uint16_t pos = 0;
  219. const uint16_t line_length = 64;
  220. while ((pos + line_length ) < cgi_buffer_size){
  221. // write position
  222. sprintf((char *) &cgi_buffer[pos], "%08"PRIx32, start_pos + pos);
  223. cgi_buffer[pos + 8] = '-';
  224. // cursor
  225. cgi_buffer[pos+cursor] = '+';
  226. cursor++;
  227. // new line
  228. cgi_buffer[pos+line_length-1] = '\n';
  229. pos += line_length;
  230. }
  231. // handle last incomplete line
  232. if (pos == 0){
  233. const char * last_line_text = "Thank you for evaluating BTstack!";
  234. uint16_t bytes_to_copy = btstack_min(strlen(last_line_text), cgi_buffer_size);
  235. memcpy(cgi_buffer, last_line_text, bytes_to_copy);
  236. pos = cgi_buffer_size;
  237. }
  238. return pos;
  239. }
  240. int fs_read_custom(struct fs_file *file, char *buffer, int count);
  241. int fs_read_custom(struct fs_file *file, char *buffer, int count)
  242. {
  243. uint32_t remaining_data = file->len - file->index;
  244. if (remaining_data == 0) {
  245. // all bytes already read
  246. return FS_READ_EOF;
  247. }
  248. uint32_t bytes_to_fill = remaining_data;
  249. if (bytes_to_fill > (uint32_t) count){
  250. bytes_to_fill = count;
  251. }
  252. uint32_t bytes_written = cgi_buffer_fill(buffer, bytes_to_fill, file->index);
  253. file->index += bytes_written;
  254. return bytes_written;
  255. }
  256. void fs_close_custom(struct fs_file *file);
  257. void fs_close_custom(struct fs_file *file){
  258. UNUSED(file);
  259. }
  260. #endif
  261. /* LISTING_END */
  262. /* @section DHCP Server Setup
  263. *
  264. * @text Listing LwipSetup shows the setup of the lwIP network stack and starts the DHCP Server
  265. */
  266. /* LISTING_START(LwipSetup): Report lwIP version, init lwIP, start DHCP Serverr */
  267. static void network_setup(void){
  268. printf("lwIP version: " LWIP_VERSION_STRING "\n");
  269. #if BYTE_ORDER == LITTLE_ENDIAN
  270. // big endian detection not supported by build configuration
  271. if (btstack_is_big_endian()){
  272. printf("lwIP configured for little endian, but running on big endian. Please set BYTE_ORDER to BIG_ENDIAN in lwiopts.h\n");
  273. while (1);
  274. }
  275. #endif
  276. // init lwIP stack
  277. #if NO_SYS
  278. lwip_init();
  279. #else
  280. tcpip_init(NULL, NULL);
  281. #endif
  282. // start DHCP Server
  283. dhserv_init(&dhcp_config);
  284. // start HTTP Server
  285. httpd_init();
  286. }
  287. /* LISTING_END */
  288. /* @section Main
  289. *
  290. * @text Setup the lwIP network and PAN NAP
  291. */
  292. /* LISTING_START(Main): Setup lwIP network and PAN NAP */
  293. int btstack_main(int argc, const char * argv[]);
  294. int btstack_main(int argc, const char * argv[]){
  295. (void)argc;
  296. (void)argv;
  297. // setup lwIP, HTTP, DHCP
  298. network_setup();
  299. // setup Classic PAN NAP Service
  300. pan_bnep_setup();
  301. // Turn on the device
  302. hci_power_control(HCI_POWER_ON);
  303. return 0;
  304. }
  305. /* LISTING_END */
  306. /* EXAMPLE_END */