1 /*
2 * Copyright (c) 2019-2021 Free Software Foundation, Inc.
3 *
4 * This file is part of GNU Wget.
5 *
6 * GNU Wget is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GNU Wget is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Wget. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <sys/types.h>
23 #include <stdint.h> // uint8_t
24 #include <stdio.h> // fmemopen
25 #include <string.h> // strncmp
26 #include <stdlib.h> // free
27 #include <unistd.h> // close
28 #include <fcntl.h> // open flags
29 #include <unistd.h> // close
30 #include <unistd.h> // close
31
32 #include "wget.h"
33 #include "connect.h"
34 #undef fopen_wgetrc
35
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39 #include "retr.h"
40
41 // declarations for wget internal functions
42 int main_wget(int argc, const char **argv);
43 void cleanup(void);
44 // FILE *fopen_wget(const char *pathname, const char *mode);
45 // FILE *fopen_wgetrc(const char *pathname, const char *mode);
46 void exit_wget(int status);
47 #ifdef __cplusplus
48 }
49 #endif
50
51 #include "fuzzer.h"
52
fopen_wget(const char * pathname,const char * mode)53 FILE *fopen_wget(const char *pathname, const char *mode)
54 {
55 return fopen("/dev/null", mode);
56 }
57
fopen_wgetrc(const char * pathname,const char * mode)58 FILE *fopen_wgetrc(const char *pathname, const char *mode)
59 {
60 return NULL;
61 }
62
63 #ifdef FUZZING
exit_wget(int status)64 void exit_wget(int status)
65 {
66 }
67 #endif
68
69 static const uint8_t *g_data;
70 static size_t g_size, g_read;
71
72 struct my_context {
73 int peeklen;
74 char peekbuf[512];
75 };
76
my_peek(int fd _GL_UNUSED,char * buf,int bufsize,void * arg,double d)77 static int my_peek (int fd _GL_UNUSED, char *buf, int bufsize, void *arg, double d)
78 {
79 (void) d;
80 if (g_read < g_size) {
81 struct my_context *ctx = (struct my_context *) arg;
82 int n = rand() % (g_size - g_read);
83 if (n > bufsize)
84 n = bufsize;
85 if (n > (int) sizeof(ctx->peekbuf))
86 n = sizeof(ctx->peekbuf);
87 memcpy(buf, g_data + g_read, n);
88 memcpy(ctx->peekbuf, g_data + g_read, n);
89 g_read += n;
90 ctx->peeklen=n;
91 return n;
92 }
93 return 0;
94 }
my_read(int fd _GL_UNUSED,char * buf,int bufsize,void * arg,double d)95 static int my_read (int fd _GL_UNUSED, char *buf, int bufsize, void *arg, double d)
96 {
97 (void) d;
98 struct my_context *ctx = (struct my_context *) arg;
99
100 if (ctx->peeklen) {
101 /* If we have any peek data, simply return that. */
102 int copysize = MIN (bufsize, ctx->peeklen);
103 memcpy (buf, ctx->peekbuf, copysize);
104 ctx->peeklen -= copysize;
105 if (ctx->peeklen)
106 memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
107
108 return copysize;
109 }
110
111 if (g_read < g_size) {
112 int n = rand() % (g_size - g_read);
113 if (n > bufsize)
114 n = bufsize;
115 memcpy(buf, g_data + g_read, n);
116 g_read += n;
117 return n;
118 }
119
120 return 0;
121 }
my_write(int fd _GL_UNUSED,char * buf _GL_UNUSED,int bufsize,void * arg _GL_UNUSED)122 static int my_write (int fd _GL_UNUSED, char *buf _GL_UNUSED, int bufsize, void *arg _GL_UNUSED)
123 {
124 return bufsize;
125 }
my_poll(int fd _GL_UNUSED,double timeout _GL_UNUSED,int wait_for _GL_UNUSED,void * arg)126 static int my_poll (int fd _GL_UNUSED, double timeout _GL_UNUSED, int wait_for _GL_UNUSED, void *arg)
127 {
128 struct my_context *ctx = (struct my_context *) arg;
129
130 return ctx->peeklen || g_read < g_size;
131 }
my_errstr(int fd _GL_UNUSED,void * arg _GL_UNUSED)132 static const char *my_errstr (int fd _GL_UNUSED, void *arg _GL_UNUSED)
133 {
134 return "Success";
135 }
my_close(int fd _GL_UNUSED,void * arg _GL_UNUSED)136 static void my_close (int fd _GL_UNUSED, void *arg _GL_UNUSED)
137 {
138 }
139
140 static struct transport_implementation my_transport =
141 {
142 my_read, my_write, my_poll,
143 my_peek, my_errstr, my_close
144 };
145
146 /* copied from wget's http.c */
147 static const char *
response_head_terminator(const char * start,const char * peeked,int peeklen)148 response_head_terminator (const char *start, const char *peeked, int peeklen)
149 {
150 const char *p, *end;
151
152 /* If at first peek, verify whether HUNK starts with "HTTP". If
153 not, this is a HTTP/0.9 request and we must bail out without
154 reading anything. */
155 if (start == peeked && 0 != memcmp (start, "HTTP", MIN (peeklen, 4)))
156 return start;
157
158 /* Look for "\n[\r]\n", and return the following position if found.
159 Start two chars before the current to cover the possibility that
160 part of the terminator (e.g. "\n\r") arrived in the previous
161 batch. */
162 p = peeked - start < 2 ? start : peeked - 2;
163 end = peeked + peeklen;
164
165 /* Check for \n\r\n or \n\n anywhere in [p, end-2). */
166 for (; p < end - 2; p++)
167 if (*p == '\n')
168 {
169 if (p[1] == '\r' && p[2] == '\n')
170 return p + 3;
171 else if (p[1] == '\n')
172 return p + 2;
173 }
174 /* p==end-2: check for \n\n directly preceding END. */
175 if (peeklen >= 2 && p[0] == '\n' && p[1] == '\n')
176 return p + 2;
177
178 return NULL;
179 }
180
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)181 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
182 {
183 char *hunk;
184
185 if (size > 4096) // same as max_len = ... in .options file
186 return 0;
187
188 // CLOSE_STDERR
189
190 g_data = data;
191 g_size = size;
192 g_read = 0;
193
194 struct my_context *ctx = (struct my_context *) calloc(1, sizeof(struct my_context));
195 fd_register_transport(99, &my_transport, ctx);
196
197 while ((hunk = fd_read_hunk(99, response_head_terminator, 512, 65536)))
198 free(hunk);
199
200 connect_cleanup();
201 free(ctx);
202
203 // RESTORE_STDERR
204
205 return 0;
206 }
207