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