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 407: /* Proxy auth */ 391 werrstr("Proxy authentication required (407)"); 392 goto Error; 393 394 case 500: /* Internal server error */ 395 werrstr("Server choked (500)"); 396 goto Error; 397 398 case 501: /* Not implemented */ 399 werrstr("Server can't do it (501)"); 400 goto Error; 401 402 case 502: /* Bad gateway */ 403 werrstr("Bad gateway (502)"); 404 goto Error; 405 406 case 503: /* Service unavailable */ 407 werrstr("Service unavailable (503)"); 408 goto Error; 409 410 default: 411 /* Bogus: we should treat unknown code XYZ as code X00 */ 412 werrstr("Unknown response code %d", code); 413 goto Error; 414 } 415 416 if(httpheaders(hs) < 0) 417 goto Error; 418 if(c->ctl.acceptcookies && hs->setcookie) 419 httpsetcookie(hs->setcookie, url->host, url->path); 420 if(authenticate){ 421 if(!hs->credentials){ 422 if(hs->autherror[0]) 423 werrstr("%s", hs->autherror); 424 else 425 werrstr("unauthorized; no www-authenticate: header"); 426 return -1; 427 } 428 c->authenticate = hs->credentials; 429 hs->credentials = nil; 430 } 431 if(redirect){ 432 if(!hs->location){ 433 werrstr("redirection without Location: header"); 434 return -1; 435 } 436 c->redirect = hs->location; 437 hs->location = nil; 438 } 439 return 0; 440 } 441 442 int 443 httpread(Client *c, Req *r) 444 { 445 char *dst; 446 HttpState *hs; 447 int n; 448 long rlen, tot, len; 449 450 hs = c->aux; 451 dst = r->ofcall.data; 452 len = r->ifcall.count; 453 tot = 0; 454 while (tot < len){ 455 rlen = len - tot; 456 n = readibuf(&hs->b, dst + tot, rlen); 457 if(n == 0) 458 break; 459 else if(n < 0){ 460 if(tot == 0) 461 return -1; 462 else 463 return tot; 464 } 465 tot += n; 466 } 467 r->ofcall.count = tot; 468 return 0; 469 } 470 471 void 472 httpclose(Client *c) 473 { 474 HttpState *hs; 475 476 hs = c->aux; 477 if(hs == nil) 478 return; 479 ioclose(c->io, hs->fd); 480 hs->fd = -1; 481 free(hs->location); 482 free(hs->setcookie); 483 free(hs->netaddr); 484 free(hs->credentials); 485 free(hs); 486 c->aux = nil; 487 } 488 489