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