/src/pupnp/fuzzer/FuzzWebServer.c
Line | Count | Source |
1 | | #include "config.h" |
2 | | |
3 | | #include "httpparser.h" |
4 | | #include "httpreadwrite.h" |
5 | | #include "sock.h" |
6 | | #include "statcodes.h" |
7 | | #include "upnpapi.h" |
8 | | #include "webserver.h" |
9 | | |
10 | | #if EXCLUDE_GENA == 0 |
11 | | #include "gena.h" |
12 | | #endif |
13 | | #if defined(INCLUDE_DEVICE_APIS) && EXCLUDE_SOAP == 0 |
14 | | #include "soaplib.h" |
15 | | #endif |
16 | | |
17 | | #include <arpa/inet.h> |
18 | | #include <netinet/in.h> |
19 | | #include <stddef.h> |
20 | | #include <stdint.h> |
21 | | #include <stdlib.h> |
22 | | #include <string.h> |
23 | | #include <sys/socket.h> |
24 | | #include <unistd.h> |
25 | | |
26 | | /* Keeps the whole input within the socket buffer, so the single write() |
27 | | * below never blocks. */ |
28 | 6.49k | #define MAX_INPUT_SIZE 65536 |
29 | | |
30 | | static const char description[] = |
31 | | "<?xml version=\"1.0\"?>\r\n" |
32 | | "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" |
33 | | "<specVersion><major>1</major><minor>0</minor></specVersion>" |
34 | | "<device>" |
35 | | "<deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>" |
36 | | "<friendlyName>fuzz</friendlyName>" |
37 | | "<UDN>uuid:fuzz-0000-0000-0000-000000000001</UDN>" |
38 | | "</device>" |
39 | | "</root>\r\n"; |
40 | | |
41 | | /* Mirrors dispatch_request() in miniserver.c. */ |
42 | | static void dispatch(SOCKINFO *info, http_parser_t *parser) |
43 | 4.69k | { |
44 | 4.69k | http_message_t *request = &parser->msg; |
45 | | |
46 | | /* The miniserver drops requests without a HOST header. */ |
47 | 4.69k | if (httpmsg_find_hdr(request, HDR_HOST, NULL) == NULL) { |
48 | 492 | return; |
49 | 492 | } |
50 | | |
51 | 4.20k | switch (request->method) { |
52 | 0 | #if defined(INCLUDE_DEVICE_APIS) && EXCLUDE_SOAP == 0 |
53 | 1 | case SOAPMETHOD_POST: |
54 | 2 | case HTTPMETHOD_MPOST: |
55 | 2 | soap_device_callback(parser, request, info); |
56 | 2 | break; |
57 | 0 | #endif |
58 | 0 | #if EXCLUDE_GENA == 0 |
59 | 2.92k | case HTTPMETHOD_NOTIFY: |
60 | 2.98k | case HTTPMETHOD_SUBSCRIBE: |
61 | 3.00k | case HTTPMETHOD_UNSUBSCRIBE: |
62 | 3.00k | genaCallback(parser, request, info); |
63 | 3.00k | break; |
64 | 0 | #endif |
65 | 0 | #if EXCLUDE_WEB_SERVER == 0 |
66 | 1.18k | case HTTPMETHOD_GET: |
67 | 1.19k | case HTTPMETHOD_POST: |
68 | 1.19k | case HTTPMETHOD_HEAD: |
69 | 1.19k | case HTTPMETHOD_SIMPLEGET: |
70 | 1.19k | web_server_callback(parser, request, info); |
71 | 1.19k | break; |
72 | 0 | #endif |
73 | 1 | default: |
74 | 1 | http_SendStatusResponse(info, |
75 | 1 | HTTP_INTERNAL_SERVER_ERROR, |
76 | 1 | request->major_version, |
77 | 1 | request->minor_version); |
78 | 1 | break; |
79 | 4.20k | } |
80 | 4.20k | } |
81 | | |
82 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
83 | 2 | { |
84 | 2 | (void)argc; |
85 | 2 | (void)argv; |
86 | | |
87 | 2 | ithread_initialize_library(); |
88 | 2 | ithread_rwlock_init(&GlobalHndRWLock, NULL); |
89 | 2 | #if EXCLUDE_WEB_SERVER == 0 |
90 | 2 | if (web_server_init() == UPNP_E_SUCCESS) { |
91 | | /* The web server takes ownership of the alias content. */ |
92 | 2 | char *doc = malloc(sizeof(description) - 1); |
93 | | |
94 | 2 | if (doc) { |
95 | 2 | memcpy(doc, description, sizeof(description) - 1); |
96 | 2 | if (web_server_set_alias("description.xml", |
97 | 2 | doc, |
98 | 2 | sizeof(description) - 1, |
99 | 2 | 0) != UPNP_E_SUCCESS) { |
100 | 0 | free(doc); |
101 | 0 | } |
102 | 2 | } |
103 | 2 | } |
104 | 2 | #endif |
105 | | |
106 | 2 | return 0; |
107 | 2 | } |
108 | | |
109 | | extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) |
110 | 6.49k | { |
111 | 6.49k | int sv[2]; |
112 | 6.49k | SOCKINFO info; |
113 | 6.49k | struct sockaddr_storage peer; |
114 | 6.49k | struct sockaddr_in *peer4 = (struct sockaddr_in *)&peer; |
115 | 6.49k | http_parser_t parser; |
116 | 6.49k | int timeout = 1; |
117 | 6.49k | int http_error_code = 0; |
118 | | |
119 | 6.49k | if (Size < 1 || Size > MAX_INPUT_SIZE) { |
120 | 0 | return 0; |
121 | 0 | } |
122 | 6.49k | parser_request_init(&parser); |
123 | 6.49k | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) { |
124 | 0 | return 0; |
125 | 0 | } |
126 | 6.49k | if (write(sv[1], Data, Size) != (ssize_t)Size) { |
127 | 0 | close(sv[0]); |
128 | 0 | close(sv[1]); |
129 | 0 | return 0; |
130 | 0 | } |
131 | | /* The end of file lets messages without a length complete. Responses |
132 | | * are left unread in sv[1] and dropped when it is closed. */ |
133 | 6.49k | shutdown(sv[1], SHUT_WR); |
134 | | |
135 | | /* GENA and SOAP pick the handle by the address family of the peer. */ |
136 | 6.49k | memset(&peer, 0, sizeof(peer)); |
137 | 6.49k | peer4->sin_family = AF_INET; |
138 | 6.49k | peer4->sin_port = htons(49152); |
139 | 6.49k | peer4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
140 | 6.49k | sock_init_with_ip(&info, sv[0], (struct sockaddr *)&peer); |
141 | | |
142 | | /* Same sequence as handle_request() in miniserver.c. */ |
143 | 6.49k | if (http_RecvMessage(&info, |
144 | 6.49k | &parser, |
145 | 6.49k | HTTPMETHOD_UNKNOWN, |
146 | 6.49k | &timeout, |
147 | 6.49k | &http_error_code) == 0) { |
148 | 4.69k | dispatch(&info, &parser); |
149 | 4.69k | } else if (http_error_code > 0) { |
150 | 1.79k | http_SendStatusResponse(&info, |
151 | 1.79k | http_error_code, |
152 | 1.79k | parser.msg.major_version, |
153 | 1.79k | parser.msg.minor_version); |
154 | 1.79k | } |
155 | | |
156 | 6.49k | httpmsg_destroy(&parser.msg); |
157 | 6.49k | sock_destroy(&info, SD_BOTH); |
158 | 6.49k | close(sv[1]); |
159 | | |
160 | 6.49k | return 0; |
161 | 6.49k | } |