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