1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ip.h> 5 #include <plumb.h> 6 #include <thread.h> 7 #include <fcall.h> 8 #include <9p.h> 9 #include <libsec.h> 10 #include <auth.h> 11 #include "dat.h" 12 #include "fns.h" 13 14 char PostContentType[] = "application/octet-stream"; 15 int httpdebug; 16 17 typedef struct HttpState HttpState; 18 struct HttpState 19 { 20 int fd; 21 Client *c; 22 char *location; 23 char *setcookie; 24 char *netaddr; 25 char *credentials; 26 char autherror[ERRMAX]; 27 Ibuf b; 28 }; 29 30 static void 31 location(HttpState *hs, char *value) 32 { 33 if(hs->location == nil) 34 hs->location = estrdup(value); 35 } 36 37 static void 38 contenttype(HttpState *hs, char *value) 39 { 40 if(hs->c->contenttype == nil) 41 hs->c->contenttype = estrdup(value); 42 } 43 44 static void 45 setcookie(HttpState *hs, char *value) 46 { 47 char *s, *t; 48 Fmt f; 49 50 s = hs->setcookie; 51 fmtstrinit(&f); 52 if(s) 53 fmtprint(&f, "%s", s); 54 fmtprint(&f, "set-cookie: "); 55 fmtprint(&f, "%s", value); 56 fmtprint(&f, "\n"); 57 t = fmtstrflush(&f); 58 if(t){ 59 free(s); 60 hs->setcookie = t; 61 } 62 } 63 64 static char* 65 unquote(char *s, char **ps) 66 { 67 char *p; 68 69 if(*s != '"'){ 70 p = strpbrk(s, " \t\r\n"); 71 *p++ = 0; 72 *ps = p; 73 return s; 74 } 75 for(p=s+1; *p; p++){ 76 if(*p == '\"'){ 77 *p++ = 0; 78 break; 79 } 80 if(*p == '\\' && *(p+1)){ 81 p++; 82 continue; 83 } 84 } 85 memmove(s, s+1, p-(s+1)); 86 s[p-(s+1)] = 0; 87 *ps = p; 88 return s; 89 } 90 91 static char* 92 servername(char *addr) 93 { 94 char *p; 95 96 if(strncmp(addr, "tcp!", 4) == 0 97 || strncmp(addr, "net!", 4) == 0) 98 addr += 4; 99 addr = estrdup(addr); 100 p = addr+strlen(addr); 101 if(p>addr && *(p-1) == 's') 102 p--; 103 if(p>addr+5 && strcmp(p-5, "!http") == 0) 104 p[-5] = 0; 105 return addr; 106 } 107 108 void 109 wwwauthenticate(HttpState *hs, char *line) 110 { 111 char cred[64], *user, *pass, *realm, *s, *spec, *name; 112 Fmt fmt; 113 UserPasswd *up; 114 115 spec = nil; 116 up = nil; 117 cred[0] = 0; 118 hs->autherror[0] = 0; 119 if(cistrncmp(line, "basic ", 6) != 0){ 120 werrstr("unknown auth: %s", line); 121 goto error; 122 } 123 line += 6; 124 if(cistrncmp(line, "realm=", 6) != 0){ 125 werrstr("missing realm: %s", line); 126 goto error; 127 } 128 line += 6; 129 user = hs->c->url->user; 130 pass = hs->c->url->passwd; 131 if(user==nil || pass==nil){ 132 realm = unquote(line, &line); 133 fmtstrinit(&fmt); 134 name = servername(hs->netaddr); 135 fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm); 136 free(name); 137 if(hs->c->url->user) 138 fmtprint(&fmt, " user=%q", hs->c->url->user); 139 spec = fmtstrflush(&fmt); 140 if(spec == nil) 141 goto error; 142 if((up = auth_getuserpasswd(nil, "%s", spec)) == nil) 143 goto error; 144 user = up->user; 145 pass = up->passwd; 146 } 147 if((s = smprint("%s:%s", user, pass)) == nil) 148 goto error; 149 free(up); 150 enc64(cred, sizeof(cred), (uchar*)s, strlen(s)); 151 memset(s, 0, strlen(s)); 152 free(s); 153 hs->credentials = smprint("Basic %s", cred); 154 if(hs->credentials == nil) 155 goto error; 156 return; 157 158 error: 159 free(up); 160 free(spec); 161 snprint(hs->autherror, sizeof hs->autherror, "%r"); 162 } 163 164 struct { 165 char *name; /* Case-insensitive */ 166 void (*fn)(HttpState *hs, char *value); 167 } hdrtab[] = { 168 { "location:", location }, 169 { "content-type:", contenttype }, 170 { "set-cookie:", setcookie }, 171 { "www-authenticate:", wwwauthenticate }, 172 }; 173 174 static int 175 httprcode(HttpState *hs) 176 { 177 int n; 178 char *p; 179 char buf[256]; 180 181 n = readline(&hs->b, buf, sizeof(buf)-1); 182 if(n <= 0) 183 return n; 184 if(httpdebug) 185 fprint(2, "-> %s\n", buf); 186 p = strchr(buf, ' '); 187 if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){ 188 werrstr("bad response from server"); 189 return -1; 190 } 191 buf[n] = 0; 192 return atoi(p+1); 193 } 194 195 /* 196 * read a single mime header, collect continuations. 197 * 198 * this routine assumes that there is a blank line twixt 199 * the header and the message body, otherwise bytes will 200 * be lost. 201 */ 202 static int 203 getheader(HttpState *hs, char *buf, int n) 204 { 205 char *p, *e; 206 int i; 207 208 n--; 209 p = buf; 210 for(e = p + n; ; p += i){ 211 i = readline(&hs->b, p, e-p); 212 if(i < 0) 213 return i; 214 215 if(p == buf){ 216 /* first line */ 217 if(strchr(buf, ':') == nil) 218 break; /* end of headers */ 219 } else { 220 /* continuation line */ 221 if(*p != ' ' && *p != '\t'){ 222 unreadline(&hs->b, p); 223 *p = 0; 224 break; /* end of this header */ 225 } 226 } 227 } 228 229 if(httpdebug) 230 fprint(2, "-> %s\n", buf); 231 return p-buf; 232 } 233 234 static int 235 httpheaders(HttpState *hs) 236 { 237 char buf[2048]; 238 char *p; 239 int i, n; 240 241 for(;;){ 242 n = getheader(hs, buf, sizeof(buf)); 243 if(n < 0) 244 return -1; 245 if(n == 0) 246 return 0; 247 // print("http header: '%.*s'\n", n, buf); 248 for(i = 0; i < nelem(hdrtab); i++){ 249 n = strlen(hdrtab[i].name); 250 if(cistrncmp(buf, hdrtab[i].name, n) == 0){ 251 /* skip field name and leading white */ 252 p = buf + n; 253 while(*p == ' ' || *p == '\t') 254 p++; 255 (*hdrtab[i].fn)(hs, p); 256 break; 257 } 258 } 259 } 260 return 0; 261 } 262 263 int 264 httpopen(Client *c, Url *url) 265 { 266 int fd, code, redirect, authenticate; 267 char *cookies; 268 Ioproc *io; 269 HttpState *hs; 270 271 if(httpdebug) 272 fprint(2, "httpopen\n"); 273 io = c->io; 274 hs = emalloc(sizeof(*hs)); 275 hs->c = c; 276 hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme)); 277 c->aux = hs; 278 if(httpdebug) 279 fprint(2, "dial %s\n", hs->netaddr); 280 fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps); 281 if(fd < 0){ 282 Error: 283 if(httpdebug) 284 fprint(2, "iodial: %r\n"); 285 free(hs->netaddr); 286 close(hs->fd); 287 hs->fd = -1; 288 free(hs); 289 c->aux = nil; 290 return -1; 291 } 292 hs->fd = fd; 293 if(httpdebug) 294 fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n", 295 c->havepostbody? "POST": " GET", url->http.page_spec, url->host); 296 ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n", 297 c->havepostbody? "POST" : "GET", url->http.page_spec, url->host); 298 if(httpdebug) 299 fprint(2, "<- User-Agent: %s\n", c->ctl.useragent); 300 if(c->ctl.useragent) 301 ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent); 302 if(c->ctl.sendcookies){ 303 /* should we use url->page here? sometimes it is nil. */ 304 cookies = httpcookies(url->host, url->http.page_spec, 0); 305 if(cookies && cookies[0]) 306 ioprint(io, fd, "%s", cookies); 307 if(httpdebug) 308 fprint(2, "<- %s", cookies); 309 free(cookies); 310 } 311 if(c->havepostbody){ 312 ioprint(io, fd, "Content-type: %s\r\n", PostContentType); 313 ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody); 314 if(httpdebug){ 315 fprint(2, "<- Content-type: %s\n", PostContentType); 316 fprint(2, "<- Content-length: %ud\n", c->npostbody); 317 } 318 } 319 if(c->authenticate){ 320 ioprint(io, fd, "Authorization: %s\r\n", c->authenticate); 321 if(httpdebug) 322 fprint(2, "<- Authorization: %s\n", c->authenticate); 323 } 324 ioprint(io, fd, "\r\n"); 325 if(c->havepostbody) 326 if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody) 327 goto Error; 328 329 c->havepostbody = 0; 330 redirect = 0; 331 authenticate = 0; 332 initibuf(&hs->b, io, fd); 333 code = httprcode(hs); 334 335 switch(code){ 336 case -1: /* connection timed out */ 337 goto Error; 338 339 /* 340 case Eof: 341 werrstr("EOF from HTTP server"); 342 goto Error; 343 */ 344 345 case 200: /* OK */ 346 case 201: /* Created */ 347 case 202: /* Accepted */ 348 case 204: /* No Content */ 349 #ifdef NOT_DEFINED 350 if(ofile == nil && r->start != 0) 351 sysfatal("page changed underfoot"); 352 #endif 353 break; 354 355 case 206: /* Partial Content */ 356 werrstr("Partial Content (206)"); 357 goto Error; 358 359 case 301: /* Moved Permanently */ 360 case 302: /* Moved Temporarily */ 361 redirect = 1; 362 break; 363 364 case 304: /* Not Modified */ 365 break; 366 367 case 400: /* Bad Request */ 368 werrstr("Bad Request (400)"); 369 goto Error; 370 371 case 401: /* Unauthorized */ 372 if(c->authenticate){ 373 werrstr("Authentication failed (401)"); 374 goto Error; 375 } 376 authenticate = 1; 377 break; 378 case 402: /* ??? */ 379 werrstr("Unauthorized (402)"); 380 goto Error; 381 382 case 403: /* Forbidden */ 383 werrstr("Forbidden by server (403)"); 384 goto Error; 385 386 case 404: /* Not Found */ 387 werrstr("Not found on server (404)"); 388 goto Error; 389 390 case 405: /* Method Not Allowed */ 391 werrstr("Method not allowed (405)"); 392 goto Error; 393 394 case 407: /* Proxy auth */ 395 werrstr("Proxy authentication required (407)"); 396 goto Error; 397 398 case 500: /* Internal server error */ 399 werrstr("Server choked (500)"); 400 goto Error; 401 402 case 501: /* Not implemented */ 403 werrstr("Server can't do it (501)"); 404 goto Error; 405 406 case 502: /* Bad gateway */ 407 werrstr("Bad gateway (502)"); 408 goto Error; 409 410 case 503: /* Service unavailable */ 411 werrstr("Service unavailable (503)"); 412 goto Error; 413 414 default: 415 /* Bogus: we should treat unknown code XYZ as code X00 */ 416 werrstr("Unknown response code %d", code); 417 goto Error; 418 } 419 420 if(httpheaders(hs) < 0) 421 goto Error; 422 if(c->ctl.acceptcookies && hs->setcookie) 423 httpsetcookie(hs->setcookie, url->host, url->path); 424 if(authenticate){ 425 if(!hs->credentials){ 426 if(hs->autherror[0]) 427 werrstr("%s", hs->autherror); 428 else 429 werrstr("unauthorized; no www-authenticate: header"); 430 return -1; 431 } 432 c->authenticate = hs->credentials; 433 hs->credentials = nil; 434 } 435 if(redirect){ 436 if(!hs->location){ 437 werrstr("redirection without Location: header"); 438 return -1; 439 } 440 c->redirect = hs->location; 441 hs->location = nil; 442 } 443 return 0; 444 } 445 446 int 447 httpread(Client *c, Req *r) 448 { 449 char *dst; 450 HttpState *hs; 451 int n; 452 long rlen, tot, len; 453 454 hs = c->aux; 455 dst = r->ofcall.data; 456 len = r->ifcall.count; 457 tot = 0; 458 while (tot < len){ 459 rlen = len - tot; 460 n = readibuf(&hs->b, dst + tot, rlen); 461 if(n == 0) 462 break; 463 else if(n < 0){ 464 if(tot == 0) 465 return -1; 466 else 467 return tot; 468 } 469 tot += n; 470 } 471 r->ofcall.count = tot; 472 return 0; 473 } 474 475 void 476 httpclose(Client *c) 477 { 478 HttpState *hs; 479 480 hs = c->aux; 481 if(hs == nil) 482 return; 483 ioclose(c->io, hs->fd); 484 hs->fd = -1; 485 free(hs->location); 486 free(hs->setcookie); 487 free(hs->netaddr); 488 free(hs->credentials); 489 free(hs); 490 c->aux = nil; 491 } 492 493