Miniupnpd v. 1.5 (20110618)
[tomato.git] / release / src / router / miniupnpd / upnphttp.c
blobb4de4f56e8b896d2b8449ae8eb7cdbc1fb320e47
1 /* $Id: upnphttp.c,v 1.60 2011/06/01 22:35:05 nanard Exp $ */
2 /* Project : miniupnp
3 * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2011 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file included in this distribution.
8 * */
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <arpa/inet.h> //!!TB
16 #include <sys/param.h>
17 #include <syslog.h>
18 #include <ctype.h>
19 #include "config.h"
20 #include "upnphttp.h"
21 #include "upnpdescgen.h"
22 #include "miniupnpdpath.h"
23 #include "upnpsoap.h"
24 #include "upnpevents.h"
26 struct upnphttp *
27 New_upnphttp(int s)
29 struct upnphttp * ret;
30 if(s<0)
31 return NULL;
32 ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
33 if(ret == NULL)
34 return NULL;
35 memset(ret, 0, sizeof(struct upnphttp));
36 ret->socket = s;
37 return ret;
40 void
41 CloseSocket_upnphttp(struct upnphttp * h)
43 if(close(h->socket) < 0)
45 syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
47 h->socket = -1;
48 h->state = 100;
51 void
52 Delete_upnphttp(struct upnphttp * h)
54 if(h)
56 if(h->socket >= 0)
57 CloseSocket_upnphttp(h);
58 if(h->req_buf)
59 free(h->req_buf);
60 if(h->res_buf)
61 free(h->res_buf);
62 free(h);
66 /* parse HttpHeaders of the REQUEST */
67 static void
68 ParseHttpHeaders(struct upnphttp * h)
70 char * line;
71 char * colon;
72 char * p;
73 int n;
74 line = h->req_buf;
75 /* TODO : check if req_buf, contentoff are ok */
76 while(line < (h->req_buf + h->req_contentoff))
78 colon = strchr(line, ':');
79 if(colon)
81 if(strncasecmp(line, "Content-Length", 14)==0)
83 p = colon;
84 while(*p < '0' || *p > '9')
85 p++;
86 h->req_contentlen = atoi(p);
87 /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
88 printf(" readbufflen=%d contentoff = %d\n",
89 h->req_buflen, h->req_contentoff);*/
91 else if(strncasecmp(line, "SOAPAction", 10)==0)
93 p = colon;
94 n = 0;
95 while(*p == ':' || *p == ' ' || *p == '\t')
96 p++;
97 while(p[n]>=' ')
99 n++;
101 if((p[0] == '"' && p[n-1] == '"')
102 || (p[0] == '\'' && p[n-1] == '\''))
104 p++; n -= 2;
106 h->req_soapAction = p;
107 h->req_soapActionLen = n;
109 #ifdef ENABLE_EVENTS
110 else if(strncasecmp(line, "Callback", 8)==0)
112 p = colon;
113 while(*p != '<' && *p != '\r' )
114 p++;
115 n = 0;
116 while(p[n] != '>' && p[n] != '\r' )
117 n++;
118 h->req_Callback = p + 1;
119 h->req_CallbackLen = MAX(0, n - 1);
121 else if(strncasecmp(line, "SID", 3)==0)
123 p = colon + 1;
124 while(isspace(*p))
125 p++;
126 n = 0;
127 while(!isspace(p[n]))
128 n++;
129 h->req_SID = p;
130 h->req_SIDLen = n;
132 /* Timeout: Seconds-nnnn */
133 /* TIMEOUT
134 Recommended. Requested duration until subscription expires,
135 either number of seconds or infinite. Recommendation
136 by a UPnP Forum working committee. Defined by UPnP vendor.
137 Consists of the keyword "Second-" followed (without an
138 intervening space) by either an integer or the keyword "infinite". */
139 else if(strncasecmp(line, "Timeout", 7)==0)
141 p = colon + 1;
142 while(isspace(*p))
143 p++;
144 if(strncasecmp(p, "Second-", 7)==0) {
145 h->req_Timeout = atoi(p+7);
148 #endif
150 while(!(line[0] == '\r' && line[1] == '\n'))
151 line++;
152 line += 2;
156 /* very minimalistic 404 error message */
157 static void
158 Send404(struct upnphttp * h)
161 static const char error404[] = "HTTP/1.1 404 Not found\r\n"
162 "Connection: close\r\n"
163 "Content-type: text/html\r\n"
164 "\r\n"
165 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
166 "<BODY><H1>Not Found</H1>The requested URL was not found"
167 " on this server.</BODY></HTML>\r\n";
168 int n;
169 n = send(h->socket, error404, sizeof(error404) - 1, 0);
170 if(n < 0)
172 syslog(LOG_ERR, "Send404: send(http): %m");
174 static const char body404[] =
175 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
176 "<BODY><H1>Not Found</H1>The requested URL was not found"
177 " on this server.</BODY></HTML>\r\n";
178 h->respflags = FLAG_HTML;
179 BuildResp2_upnphttp(h, 404, "Not Found",
180 body404, sizeof(body404) - 1);
181 SendResp_upnphttp(h);
182 CloseSocket_upnphttp(h);
185 /* very minimalistic 501 error message */
186 static void
187 Send501(struct upnphttp * h)
190 static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n"
191 "Connection: close\r\n"
192 "Content-type: text/html\r\n"
193 "\r\n"
194 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
195 "<BODY><H1>Not Implemented</H1>The HTTP Method "
196 "is not implemented by this server.</BODY></HTML>\r\n";
197 int n;
198 n = send(h->socket, error501, sizeof(error501) - 1, 0);
199 if(n < 0)
201 syslog(LOG_ERR, "Send501: send(http): %m");
204 static const char body501[] =
205 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
206 "<BODY><H1>Not Implemented</H1>The HTTP Method "
207 "is not implemented by this server.</BODY></HTML>\r\n";
208 h->respflags = FLAG_HTML;
209 BuildResp2_upnphttp(h, 501, "Not Implemented",
210 body501, sizeof(body501) - 1);
211 SendResp_upnphttp(h);
212 CloseSocket_upnphttp(h);
215 static const char *
216 findendheaders(const char * s, int len)
218 while(len-->0)
220 if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
221 return s;
222 s++;
224 return NULL;
227 #ifdef HAS_DUMMY_SERVICE
228 static void
229 sendDummyDesc(struct upnphttp * h)
231 static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
232 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
233 " <specVersion>"
234 " <major>1</major>"
235 " <minor>0</minor>"
236 " </specVersion>"
237 " <actionList />"
238 " <serviceStateTable />"
239 "</scpd>\r\n";
240 BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
241 SendResp_upnphttp(h);
242 CloseSocket_upnphttp(h);
244 #endif
246 /* Sends the description generated by the parameter */
247 static void
248 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
250 char * desc;
251 int len;
252 desc = f(&len);
253 if(!desc)
255 static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
256 "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
257 syslog(LOG_ERR, "Failed to generate XML description");
258 h->respflags = FLAG_HTML;
259 BuildResp2_upnphttp(h, 500, "Internal Server Error",
260 error500, sizeof(error500)-1);
262 else
264 BuildResp_upnphttp(h, desc, len);
266 SendResp_upnphttp(h);
267 CloseSocket_upnphttp(h);
268 free(desc);
271 /* ProcessHTTPPOST_upnphttp()
272 * executes the SOAP query if it is possible */
273 static void
274 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
276 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
278 if(h->req_soapAction)
280 /* we can process the request */
281 syslog(LOG_INFO, "SOAPAction: %.*s",
282 h->req_soapActionLen, h->req_soapAction);
283 ExecuteSoapAction(h,
284 h->req_soapAction,
285 h->req_soapActionLen);
287 else
289 static const char err400str[] =
290 "<html><body>Bad request</body></html>";
291 syslog(LOG_INFO, "No SOAPAction in HTTP headers");
292 h->respflags = FLAG_HTML;
293 BuildResp2_upnphttp(h, 400, "Bad Request",
294 err400str, sizeof(err400str) - 1);
295 SendResp_upnphttp(h);
296 CloseSocket_upnphttp(h);
299 else
301 /* waiting for remaining data */
302 h->state = 1;
306 #ifdef ENABLE_EVENTS
307 static void
308 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
310 const char * sid;
311 syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
312 syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
313 h->req_CallbackLen, h->req_Callback, h->req_Timeout);
314 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
315 if(!h->req_Callback && !h->req_SID) {
316 /* Missing or invalid CALLBACK : 412 Precondition Failed.
317 * If CALLBACK header is missing or does not contain a valid HTTP URL,
318 * the publisher must respond with HTTP error 412 Precondition Failed*/
319 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
320 SendResp_upnphttp(h);
321 CloseSocket_upnphttp(h);
322 } else {
323 /* - add to the subscriber list
324 * - respond HTTP/x.x 200 OK
325 * - Send the initial event message */
326 /* Server:, SID:; Timeout: Second-(xx|infinite) */
327 if(h->req_Callback) {
328 sid = upnpevents_addSubscriber(path, h->req_Callback,
329 h->req_CallbackLen, h->req_Timeout);
330 h->respflags = FLAG_TIMEOUT;
331 if(sid) {
332 syslog(LOG_DEBUG, "generated sid=%s", sid);
333 h->respflags |= FLAG_SID;
334 h->req_SID = sid;
335 h->req_SIDLen = strlen(sid);
337 BuildResp_upnphttp(h, 0, 0);
338 } else {
339 /* subscription renew */
340 /* Invalid SID
341 412 Precondition Failed. If a SID does not correspond to a known,
342 un-expired subscription, the publisher must respond
343 with HTTP error 412 Precondition Failed. */
344 if(renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0) {
345 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
346 } else {
347 h->respflags = FLAG_TIMEOUT;
348 BuildResp_upnphttp(h, 0, 0);
351 SendResp_upnphttp(h);
352 CloseSocket_upnphttp(h);
356 static void
357 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
359 syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
360 syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
361 /* Remove from the list */
362 if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0) {
363 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
364 } else {
365 BuildResp_upnphttp(h, 0, 0);
367 SendResp_upnphttp(h);
368 CloseSocket_upnphttp(h);
370 #endif
372 /* Parse and process Http Query
373 * called once all the HTTP headers have been received. */
374 static void
375 ProcessHttpQuery_upnphttp(struct upnphttp * h)
377 char HttpCommand[16];
378 char HttpUrl[128];
379 char * HttpVer;
380 char * p;
381 int i;
382 p = h->req_buf;
383 if(!p)
384 return;
385 for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
386 HttpCommand[i] = *(p++);
387 HttpCommand[i] = '\0';
388 while(*p==' ')
389 p++;
390 for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
391 HttpUrl[i] = *(p++);
392 HttpUrl[i] = '\0';
393 while(*p==' ')
394 p++;
395 HttpVer = h->HttpVer;
396 for(i = 0; i<15 && *p != '\r'; i++)
397 HttpVer[i] = *(p++);
398 HttpVer[i] = '\0';
399 syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
400 HttpCommand, HttpUrl, HttpVer);
401 ParseHttpHeaders(h);
402 if(strcmp("POST", HttpCommand) == 0)
404 h->req_command = EPost;
405 ProcessHTTPPOST_upnphttp(h);
407 else if(strcmp("GET", HttpCommand) == 0)
409 h->req_command = EGet;
410 if(strcasecmp(ROOTDESC_PATH, HttpUrl) == 0)
412 sendXMLdesc(h, genRootDesc);
414 else if(strcasecmp(WANIPC_PATH, HttpUrl) == 0)
416 sendXMLdesc(h, genWANIPCn);
418 else if(strcasecmp(WANCFG_PATH, HttpUrl) == 0)
420 sendXMLdesc(h, genWANCfg);
422 #ifdef HAS_DUMMY_SERVICE
423 else if(strcasecmp(DUMMY_PATH, HttpUrl) == 0)
425 sendDummyDesc(h);
427 #endif
428 #ifdef ENABLE_L3F_SERVICE
429 else if(strcasecmp(L3F_PATH, HttpUrl) == 0)
431 sendXMLdesc(h, genL3F);
433 #endif
434 #ifdef ENABLE_6FC_SERVICE
435 else if(strcasecmp(WANIP6FC_PATH, HttpUrl) == 0)
437 sendXMLdesc(h, gen6FC);
439 #endif
440 #ifdef ENABLE_DP_SERVICE
441 else if(strcasecmp(DP_PATH, HttpUrl) == 0)
443 sendXMLdesc(h, genDP);
445 #endif
446 else
448 syslog(LOG_INFO, "%s not found, responding ERROR 404", HttpUrl);
449 Send404(h);
452 #ifdef ENABLE_EVENTS
453 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
455 h->req_command = ESubscribe;
456 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
458 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
460 h->req_command = EUnSubscribe;
461 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
463 #else
464 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
466 syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
467 Send501(h);
469 #endif
470 else
472 syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
473 Send501(h);
478 void
479 Process_upnphttp(struct upnphttp * h)
481 char buf[2048];
482 int n;
483 if(!h)
484 return;
485 switch(h->state)
487 case 0:
488 n = recv(h->socket, buf, 2048, 0);
489 if(n<0)
491 syslog(LOG_ERR, "recv (state0): %m");
492 h->state = 100;
494 else if(n==0)
496 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
497 inet_ntoa(h->clientaddr)); //!!TB - added client address
498 h->state = 100;
500 else
502 const char * endheaders;
503 /* if 1st arg of realloc() is null,
504 * realloc behaves the same as malloc() */
505 h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
506 memcpy(h->req_buf + h->req_buflen, buf, n);
507 h->req_buflen += n;
508 h->req_buf[h->req_buflen] = '\0';
509 /* search for the string "\r\n\r\n" */
510 endheaders = findendheaders(h->req_buf, h->req_buflen);
511 if(endheaders)
513 h->req_contentoff = endheaders - h->req_buf + 4;
514 ProcessHttpQuery_upnphttp(h);
517 break;
518 case 1:
519 n = recv(h->socket, buf, 2048, 0);
520 if(n<0)
522 syslog(LOG_ERR, "recv (state1): %m");
523 h->state = 100;
525 else if(n==0)
527 syslog(LOG_WARNING, "HTTP Connection from %s closed unexpectedly",
528 inet_ntoa(h->clientaddr)); //!!TB - added client address
529 h->state = 100;
531 else
533 /*fwrite(buf, 1, n, stdout);*/ /* debug */
534 h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
535 memcpy(h->req_buf + h->req_buflen, buf, n);
536 h->req_buflen += n;
537 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
539 ProcessHTTPPOST_upnphttp(h);
542 break;
543 default:
544 syslog(LOG_WARNING, "Unexpected state: %d", h->state);
548 static const char httpresphead[] =
549 "%s %d %s\r\n"
550 /*"Content-Type: text/xml; charset=\"utf-8\"\r\n"*/
551 "Content-Type: %s\r\n"
552 "Connection: close\r\n"
553 "Content-Length: %d\r\n"
554 /*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/
555 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
556 ; /*"\r\n";*/
558 "<?xml version=\"1.0\"?>\n"
559 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
560 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
561 "<s:Body>"
563 "</s:Body>"
564 "</s:Envelope>";
566 /* with response code and response message
567 * also allocate enough memory */
569 void
570 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
571 const char * respmsg,
572 int bodylen)
574 int templen;
575 if(!h->res_buf)
577 templen = sizeof(httpresphead) + 128 + bodylen;
578 h->res_buf = (char *)malloc(templen);
579 h->res_buf_alloclen = templen;
581 h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
582 httpresphead, h->HttpVer,
583 respcode, respmsg,
584 (h->respflags&FLAG_HTML)?"text/html":"text/xml",
585 bodylen);
586 /* Additional headers */
587 #ifdef ENABLE_EVENTS
588 if(h->respflags & FLAG_TIMEOUT) {
589 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
590 h->res_buf_alloclen - h->res_buflen,
591 "Timeout: Second-");
592 if(h->req_Timeout) {
593 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
594 h->res_buf_alloclen - h->res_buflen,
595 "%d\r\n", h->req_Timeout);
596 } else {
597 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
598 h->res_buf_alloclen - h->res_buflen,
599 "infinite\r\n");
602 if(h->respflags & FLAG_SID) {
603 h->res_buflen += snprintf(h->res_buf + h->res_buflen,
604 h->res_buf_alloclen - h->res_buflen,
605 "SID: %s\r\n", h->req_SID);
607 #endif
608 h->res_buf[h->res_buflen++] = '\r';
609 h->res_buf[h->res_buflen++] = '\n';
610 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
612 h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
613 h->res_buf_alloclen = h->res_buflen + bodylen;
617 void
618 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
619 const char * respmsg,
620 const char * body, int bodylen)
622 BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
623 if(body)
624 memcpy(h->res_buf + h->res_buflen, body, bodylen);
625 h->res_buflen += bodylen;
628 /* responding 200 OK ! */
629 void
630 BuildResp_upnphttp(struct upnphttp * h,
631 const char * body, int bodylen)
633 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
636 void
637 SendResp_upnphttp(struct upnphttp * h)
639 int n;
640 n = send(h->socket, h->res_buf, h->res_buflen, 0);
641 if(n<0)
643 syslog(LOG_ERR, "send(res_buf): %m");
645 else if(n < h->res_buflen)
647 /* TODO : handle correctly this case */
648 syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
649 n, h->res_buflen);