1 /*
2 * lws-minimal-raw-adopt-udp
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * This demonstrates integrating a connected udp
10 * socket into the lws event loop as a RAW wsi. It's interesting in
11 * the kind of situation where you already have a connected socket
12 * in your application, and you need to hand it over to lws to deal with.
13 *
14 * Lws supports "adopting" these foreign sockets, and also has a helper API
15 * to create, bind, and adopt them inside lws.
16 */
17
18 #include <libwebsockets.h>
19 #include <string.h>
20 #include <signal.h>
21 #if !defined(WIN32)
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <arpa/inet.h>
26 #endif
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33
34 static uint8_t sendbuf[4096];
35 static size_t sendlen;
36 struct lws_udp udp;
37
38 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)39 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
40 void *user, void *in, size_t len)
41 {
42 ssize_t n;
43 int fd;
44
45 switch (reason) {
46
47 /* callbacks related to raw socket descriptor */
48
49 case LWS_CALLBACK_RAW_ADOPT:
50 lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
51 break;
52
53 case LWS_CALLBACK_RAW_CLOSE:
54 lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
55 break;
56
57 case LWS_CALLBACK_RAW_RX:
58 lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
59 lwsl_hexdump_level(LLL_NOTICE, in, len);
60 /*
61 * Take a copy of the buffer and the source socket address...
62 */
63 udp = *(lws_get_udp(wsi));
64 sendlen = len;
65 if (sendlen > sizeof(sendbuf))
66 sendlen = sizeof(sendbuf);
67 memcpy(sendbuf, in, sendlen);
68 /*
69 * ... and we send it next time around the event loop. This
70 * can be extended to having a ringbuffer of different send
71 * buffers and targets queued.
72 *
73 * Note that UDP is ALWAYS writable as far as poll() knows
74 * because there is no mechanism like the tcp window to
75 * understand that packets are not being acknowledged. But
76 * this allows the event loop to share out the work.
77 */
78 lws_callback_on_writable(wsi);
79 break;
80
81 case LWS_CALLBACK_RAW_WRITEABLE:
82
83 if (!sendlen)
84 break;
85
86 fd = lws_get_socket_fd(wsi);
87 if (fd < 0) /* keep Coverity happy: actually it cannot be < 0 */
88 break;
89
90 /*
91 * We can write directly on the UDP socket, specifying
92 * the peer the write is directed to.
93 *
94 * However the kernel may only accept parts of large sendto()s,
95 * leaving you to try to resend the remainder later. However
96 * depending on how your protocol on top of UDP works, that
97 * may involve sticking new headers before the remainder.
98 *
99 * For clarity partial sends just drop the remainder here.
100 */
101 n = sendto(fd,
102 #if defined(WIN32)
103 (const char *)
104 #endif
105 sendbuf, sendlen, 0, &udp.sa, udp.salen);
106 if (n < (ssize_t)len)
107 lwsl_notice("%s: send returned %d\n", __func__, (int)n);
108 break;
109
110 default:
111 break;
112 }
113
114 return 0;
115 }
116
117 static struct lws_protocols protocols[] = {
118 { "raw-test", callback_raw_test, 0, 0 },
119 { NULL, NULL, 0, 0 } /* terminator */
120 };
121
122 static int interrupted;
123
sigint_handler(int sig)124 void sigint_handler(int sig)
125 {
126 interrupted = 1;
127 }
128
main(int argc,const char ** argv)129 int main(int argc, const char **argv)
130 {
131 struct lws_context_creation_info info;
132 struct lws_context *context;
133 struct lws_vhost *vhost;
134 const char *p;
135 int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
136 /* for LLL_ verbosity above NOTICE to be built into lws,
137 * lws must have been configured and built with
138 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
139 /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
140 /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
141 /* | LLL_DEBUG */;
142
143 signal(SIGINT, sigint_handler);
144
145 if ((p = lws_cmdline_option(argc, argv, "-d")))
146 logs = atoi(p);
147
148 lws_set_log_level(logs, NULL);
149 lwsl_user("LWS minimal raw adopt udp | nc -u 127.0.0.1 7681\n");
150
151 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
152 info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
153
154 context = lws_create_context(&info);
155 if (!context) {
156 lwsl_err("lws init failed\n");
157 return 1;
158 }
159
160 info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
161 info.protocols = protocols;
162
163 vhost = lws_create_vhost(context, &info);
164 if (!vhost) {
165 lwsl_err("lws vhost creation failed\n");
166 goto bail;
167 }
168
169 /*
170 * Create our own "foreign" UDP socket bound to 7681/udp
171 */
172 if (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND,
173 protocols[0].name, NULL, NULL, NULL, NULL)) {
174 lwsl_err("%s: foreign socket adoption failed\n", __func__);
175 goto bail;
176 }
177
178 while (n >= 0 && !interrupted)
179 n = lws_service(context, 0);
180
181 bail:
182 lws_context_destroy(context);
183
184 return 0;
185 }
186