chiark / gitweb /
regress: fuzzraw: Handle select and poll return value specially
[adns.git] / regress / hplayback.c.m4
1 m4_dnl hplayback.c.m4
2 m4_dnl (part of complex test harness, not of the library)
3 m4_dnl - playback routines
4
5 m4_dnl  This file is part of adns, which is
6 m4_dnl    Copyright (C) 1997-2000,2003,2006,2014-2016,2020  Ian Jackson
7 m4_dnl    Copyright (C) 2014  Mark Wooding
8 m4_dnl    Copyright (C) 1999-2000,2003,2006  Tony Finch
9 m4_dnl    Copyright (C) 1991 Massachusetts Institute of Technology
10 m4_dnl  (See the file INSTALL for full details.)
11 m4_dnl  
12 m4_dnl  This program is free software; you can redistribute it and/or modify
13 m4_dnl  it under the terms of the GNU General Public License as published by
14 m4_dnl  the Free Software Foundation; either version 3, or (at your option)
15 m4_dnl  any later version.
16 m4_dnl  
17 m4_dnl  This program is distributed in the hope that it will be useful,
18 m4_dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 m4_dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 m4_dnl  GNU General Public License for more details.
21 m4_dnl  
22 m4_dnl  You should have received a copy of the GNU General Public License
23 m4_dnl  along with this program; if not, write to the Free Software Foundation.
24
25 m4_include(hmacros.i4)
26
27 #include <assert.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdlib.h>
31
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <sys/time.h>
37
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <limits.h>
41
42
43 #include "harness.h"
44
45 static FILE *Tinputfile, *Tfuzzrawfile, *Treportfile;
46 static vbuf vb2;
47
48 static void Tensurereportfile(void) {
49   const char *fdstr;
50   int fd;
51
52   if (Treportfile) return;
53   Treportfile= stderr;
54   fdstr= getenv("ADNS_TEST_REPORT_FD"); if (!fdstr) return;
55   fd= atoi(fdstr);
56   Treportfile= fdopen(fd,"a"); if (!Treportfile) Tfailed("fdopen ADNS_TEST_REPORT_FD");
57 }
58
59 static void Tensurefuzzrawfile(void) {
60   static int done;
61
62   if (done) return;
63   done++;
64
65   const char *fdstr= getenv("ADNS_TEST_FUZZRAW_DUMP_FD");
66   if (!fdstr) return;
67
68   int fd= atoi(fdstr);
69   Tfuzzrawfile= fdopen(fd,"ab");
70   if (!Tfuzzrawfile) Tfailed("fdopen ADNS_TEST_FUZZRAW_DUMP_FD");
71 }
72
73 static void FR_write(void *p, size_t sz) {
74   if (!Tfuzzrawfile) return;
75   ssize_t got = fwrite(p,1,sz,Tfuzzrawfile);
76   if (ferror(Tfuzzrawfile)) Tfailed("write fuzzraw output file");
77   assert(got==sz);
78 }
79
80 #define FR_WRITE(x) (FR_write(&(x), sizeof((x))))
81
82 extern void Tshutdown(void) {
83   adns__vbuf_free(&vb2);
84   if (Tfuzzrawfile) {
85     if (fclose(Tfuzzrawfile)) Tfailed("close fuzzraw output file");
86   }
87 }
88
89 static void Psyntax(const char *where) {
90   fprintf(stderr,"adns test harness: syntax error in test log input file: %s\n",where);
91   exit(-1);
92 }
93
94 static void Pcheckinput(void) {
95   if (ferror(Tinputfile)) Tfailed("read test log input file");
96   if (feof(Tinputfile)) Psyntax("eof at syscall reply");
97 }
98
99 void T_gettimeofday_hook(void) {
100   static struct timeval previously;
101   struct timeval delta;
102   memset(&delta,0,sizeof(delta));
103   timersub(&currenttime, &previously, &delta);
104   FR_WRITE(delta);
105 }
106
107 void Tensurerecordfile(void) {
108   int fd;
109   int chars;
110   unsigned long sec, usec;
111
112   if (Tinputfile) return;
113   Tinputfile= stdin;
114   fd = Ttestinputfd();
115   if (fd >= 0) {
116     Tinputfile= fdopen(fd,"r"); if (!Tinputfile) Tfailed("fdopen ADNS_TEST_IN_FD");
117   }
118   setvbuf(Tinputfile,0,_IONBF,0);
119
120   if (!adns__vbuf_ensure(&vb2,1000)) Tnomem();
121   fgets(vb2.buf,vb2.avail,Tinputfile); Pcheckinput();
122   chars= -1;
123   sscanf(vb2.buf," start %lu.%lu%n",&sec,&usec,&chars);
124   if (chars==-1) Psyntax("start time invalid");
125   currenttime.tv_sec= sec;
126   currenttime.tv_usec= usec;
127   if (vb2.buf[chars] != hm_squote\nhm_squote) Psyntax("not newline after start time");
128 }
129
130 static void Parg(const char *argname) {
131   int l;
132
133   if (vb2.buf[vb2.used++] != hm_squote hm_squote) Psyntax("not a space before argument");
134   l= strlen(argname);
135   if (memcmp(vb2.buf+vb2.used,argname,l)) Psyntax("argument name wrong");
136   vb2.used+= l;
137   if (vb2.buf[vb2.used++] != hm_squote=hm_squote) Psyntax("not = after argument name");
138 }
139
140 static int Pstring_maybe(const char *string) {
141   int l;
142
143   l= strlen(string);
144   if (memcmp(vb2.buf+vb2.used,string,l)) return 0;
145   vb2.used+= l;
146   return 1;
147 }
148
149 static void Pstring(const char *string, const char *emsg) {
150   if (Pstring_maybe(string)) return;
151   Psyntax(emsg);
152 }
153
154 static int Perrno(const char *stuff) {
155   const struct Terrno *te;
156   int r;
157   char *ep;
158
159   for (te= Terrnos; te->n && strcmp(te->n,stuff); te++);
160   if (te->n) return te->v;
161   r= strtoul(stuff+2,&ep,10);
162   if (*ep) Psyntax("errno value not recognised, not numeric");
163   if (r==0 || r>255) Psyntax("numeric errno out of range 1..255");
164   return r;
165 }
166
167 static void P_updatetime(void) {
168   int chars;
169   unsigned long sec, usec;
170
171   if (!adns__vbuf_ensure(&vb2,1000)) Tnomem();
172   fgets(vb2.buf,vb2.avail,Tinputfile); Pcheckinput();
173   chars= -1;
174   sscanf(vb2.buf," +%lu.%lu%n",&sec,&usec,&chars);
175   if (chars==-1) Psyntax("update time invalid");
176   currenttime.tv_sec+= sec;
177   currenttime.tv_usec+= usec;
178   if (currenttime.tv_usec > 1000000) {
179     currenttime.tv_sec++;
180     currenttime.tv_usec -= 1000000;
181   }
182   if (vb2.buf[chars] != hm_squote\nhm_squote) Psyntax("not newline after update time");
183 }
184
185 static void Pfdset(fd_set *set, int max) {
186   int c;
187   unsigned long ul;
188   char *ep;
189
190   if (!set) {
191     Pstring("null","null fdset pointer");
192     return;
193   }
194   
195   if (vb2.buf[vb2.used++] != hm_squote[hm_squote) Psyntax("fd set start not [");
196   FD_ZERO(set);
197   if (vb2.buf[vb2.used] == hm_squote]hm_squote) { vb2.used++; return; }
198   for (;;) {
199     ul= strtoul(vb2.buf+vb2.used,&ep,10);
200     if (ul>=max) Psyntax("fd set member > max");
201     if (ep == (char*)vb2.buf+vb2.used) Psyntax("empty entry in fd set");
202     FD_SET(ul,set);
203     vb2.used= ep - (char*)vb2.buf;
204     c= vb2.buf[vb2.used++];
205     if (c == hm_squote]hm_squote) break;
206     if (c != hm_squote,hm_squote) Psyntax("fd set separator not ,");
207   }
208 }
209
210 #ifdef HAVE_POLL
211 static int Ppollfdevents(void) {
212   int events;
213
214   if (Pstring_maybe("0")) return 0;
215   events= 0;
216
217   if (Pstring_maybe("POLLIN")) {
218     events |= POLLIN;
219     if (!Pstring_maybe("|")) return events;
220   }
221
222   if (Pstring_maybe("POLLOUT")) {
223     events |= POLLOUT;
224     if (!Pstring_maybe("|")) return events;
225   }
226
227   Pstring("POLLPRI","pollfdevents PRI?");
228   return events;
229 }
230
231 static void Ppollfds(struct pollfd *fds, int nfds) {
232   int i;
233   char *ep;
234   const char *comma= "";
235   
236   if (vb2.buf[vb2.used++] != hm_squote[hm_squote) Psyntax("pollfds start not [");
237   for (i=0; i<nfds; i++) {
238     Pstring("{fd=","{fd= in pollfds");
239     fds->fd= strtoul(vb2.buf+vb2.used,&ep,10);
240     vb2.used= ep - (char*)vb2.buf;    
241     Pstring(", events=",", events= in pollfds");
242     fds->events= Ppollfdevents();
243     Pstring(", revents=",", revents= in pollfds");
244     fds->revents= Ppollfdevents();
245     Pstring("}","} in pollfds");
246     Pstring(comma,"separator in pollfds");
247     comma= ", ";
248   }
249   if (vb2.buf[vb2.used++] != hm_squote]hm_squote) Psyntax("pollfds end not ]");
250 }
251 #endif
252
253 static void Paddr(struct sockaddr *addr, int *lenr) {
254   adns_rr_addr a;
255   char *p, *q, *ep;
256   int err;
257   unsigned long ul;
258
259   p= vb2.buf+vb2.used;
260   if (*p!='[') {
261     q= strchr(p,':');
262     if (!q) Psyntax("missing :");
263     *q++= 0;
264   } else {
265     p++;
266     q= strchr(p,']');
267     if (!q) Psyntax("missing ]");
268     *q++= 0;
269     if (*q!=':') Psyntax("expected : after ]");
270     q++;
271   }
272   ul= strtoul(q,&ep,10);
273   if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
274   if (ul >= 65536) Psyntax("port too large");
275
276   a.len= sizeof(a.addr);
277   err= adns_text2addr(p, (int)ul, 0, &a.addr.sa,&a.len);
278   if (err) Psyntax("invalid address");
279
280   assert(*lenr >= a.len);
281   memcpy(addr, &a.addr, a.len);
282   *lenr= a.len;
283   vb2.used= ep - (char*)vb2.buf;
284 }
285
286 static int Pbytes(byte *buf, int maxlen) {
287   static const char hexdigits[]= "0123456789abcdef";
288
289   int c, v, done;
290   const char *pf;
291
292   done= 0;
293   for (;;) {
294     c= getc(Tinputfile); Pcheckinput();
295     if (c=='\n' || c==' ' || c=='\t') continue;
296     if (c=='.') break;
297     pf= strchr(hexdigits,c); if (!pf) Psyntax("invalid first hex digit");
298     v= (pf-hexdigits)<<4;
299     c= getc(Tinputfile); Pcheckinput();
300     pf= strchr(hexdigits,c); if (!pf) Psyntax("invalid second hex digit");
301     v |= (pf-hexdigits);
302     if (maxlen<=0) Psyntax("buffer overflow in bytes");
303     *buf++= v;
304     maxlen--; done++;
305   }
306   for (;;) {
307     c= getc(Tinputfile); Pcheckinput();
308     if (c=='\n') return done;
309   }
310 }
311   
312 void Q_vb(void) {
313   const char *nl;
314
315   Tensurerecordfile();
316   if (!adns__vbuf_ensure(&vb2,vb.used+2)) Tnomem();
317   fread(vb2.buf,1,vb.used+2,Tinputfile);
318   if (feof(Tinputfile)) {
319     fprintf(stderr,"adns test harness: input ends prematurely; program did:\n %.*s\n",
320            vb.used,vb.buf);
321     exit(-1);
322   }
323   Pcheckinput();
324   if (vb2.buf[0] != hm_squote hm_squote) Psyntax("not space before call");
325   if (memcmp(vb.buf,vb2.buf+1,vb.used) ||
326       vb2.buf[vb.used+1] != hm_squote\nhm_squote) {
327     fprintf(stderr,
328             "adns test harness: program did unexpected:\n %.*s\n"
329             "was expecting:\n %.*s\n",
330             vb.used,vb.buf, vb.used,vb2.buf+1);
331     exit(1);
332   }
333   Tensurereportfile();
334   nl= memchr(vb.buf,'\n',vb.used);
335   fprintf(Treportfile," %.*s\n", (int)(nl ? nl - (const char*)vb.buf : vb.used), vb.buf);
336 }
337
338 m4_define(`hm_syscall', `
339  hm_create_proto_h
340 int H$1(hm_args_massage($3,void)) {
341  int r, amtread;
342  hm_create_nothing
343  m4_define(`hm_rv_fd',`char *ep;')
344  m4_define(`hm_rv_any',`char *ep;')
345  $2
346
347  hm_create_hqcall_vars
348  $3
349
350  hm_create_hqcall_init($1)
351  $3
352
353  hm_create_hqcall_args
354  Q$1(hm_args_massage($3));
355
356  m4_define(`hm_r_offset',`m4_len(` $1=')')
357  if (!adns__vbuf_ensure(&vb2,1000)) Tnomem();
358  fgets(vb2.buf,vb2.avail,Tinputfile); Pcheckinput();
359
360  Tensurereportfile();
361  Tensurefuzzrawfile();
362  fprintf(Treportfile,"%s",vb2.buf);
363  amtread= strlen(vb2.buf);
364  if (amtread<=0 || vb2.buf[--amtread]!=hm_squote\nhm_squote)
365   Psyntax("badly formed line");
366  vb2.buf[amtread]= 0;
367  if (memcmp(vb2.buf," $1=",hm_r_offset)) Psyntax("syscall reply mismatch");
368
369  m4_define(`hm_rv_check_errno',`
370  if (vb2.buf[hm_r_offset] == hm_squoteEhm_squote) {
371   int e;
372   e= Perrno(vb2.buf+hm_r_offset);
373   P_updatetime();
374   errno= e;
375   FR_WRITE(e);
376   return -1;
377  }
378  r= 0;
379  FR_WRITE(r);
380  ')
381  m4_define(`hm_rv_check_success',`
382   if (memcmp(vb2.buf+hm_r_offset,"OK",2)) Psyntax("success/fail not E* or OK");
383   vb2.used= hm_r_offset+2;
384   r= 0;
385  ')
386  m4_define(`hm_rv_any_nowrite',`
387   hm_rv_check_errno
388   unsigned long ul_r= strtoul(vb2.buf+hm_r_offset,&ep,10);
389   if (ul_r < 0 || ul_r > INT_MAX ||
390       (*ep && *ep!=hm_squote hm_squote))
391     Psyntax("return value not E* or positive number");
392   r= ul_r;
393   vb2.used= ep - (char*)vb2.buf;
394  ')
395
396  m4_define(`hm_rv_succfail',`
397   hm_rv_check_errno
398   hm_rv_check_success
399  ')
400  m4_define(`hm_rv_len',`
401   hm_rv_check_errno
402   hm_rv_check_success
403  ')
404  m4_define(`hm_rv_must',`
405   hm_rv_check_success
406  ')
407  m4_define(`hm_rv_any',`
408   hm_rv_any_nowrite
409   FR_WRITE(r);
410  ')
411  m4_define(`hm_rv_fd',`hm_rv_any')
412  m4_define(`hm_rv_select',`hm_rv_any_nowrite')
413  m4_define(`hm_rv_poll',`hm_rv_any_nowrite')
414  m4_define(`hm_rv_fcntl',`
415   r= 0;
416   if (cmd == F_GETFL) {
417     if (!memcmp(vb2.buf+hm_r_offset,"O_NONBLOCK|...",14)) {
418       r= O_NONBLOCK;
419       vb2.used= hm_r_offset+14;
420     } else if (!memcmp(vb2.buf+hm_r_offset,"~O_NONBLOCK&...",15)) {
421       vb2.used= hm_r_offset+15;
422     } else {
423       Psyntax("fcntl flags not O_NONBLOCK|... or ~O_NONBLOCK&...");
424     }
425   } else if (cmd == F_SETFL) {
426     hm_rv_succfail
427   } else {
428     Psyntax("fcntl not F_GETFL or F_SETFL");
429   }
430  ')
431  $2
432
433  hm_create_nothing
434  m4_define(`hm_arg_fdset_io',`Parg("$'`1"); Pfdset($'`1,$'`2);')
435  m4_define(`hm_arg_pollfds_io',`Parg("$'`1"); Ppollfds($'`1,$'`2);')
436  m4_define(`hm_arg_addr_out',`Parg("$'`1"); Paddr($'`1,$'`2);')
437  $3
438  assert(vb2.used <= amtread);
439  if (vb2.used != amtread) Psyntax("junk at end of line");
440
441  hm_create_nothing
442  m4_define(`hm_arg_bytes_out',`
443   r= Pbytes($'`2,$'`4);
444   FR_WRITE(r);
445   FR_write(buf,r);
446  ')
447  $3
448
449  P_updatetime();
450  return r;
451 }
452 ')
453
454 m4_define(`hm_specsyscall', `')
455
456 m4_include(`hsyscalls.i4')
457
458 hm_stdsyscall_close