kurl.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <ctype.h>
  4. #include <assert.h>
  5. #include <stdint.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <curl/curl.h>
  10. #include "kurl.h"
  11. /**********************
  12. *** Core kurl APIs ***
  13. **********************/
  14. #define KU_DEF_BUFLEN 0x8000
  15. #define KU_MAX_SKIP (KU_DEF_BUFLEN<<1) // if seek step is smaller than this, skip
  16. #define kurl_isfile(u) ((u)->fd >= 0)
  17. #ifndef kroundup32
  18. #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
  19. #endif
  20. struct kurl_t {
  21. CURLM *multi; // cURL multi handler
  22. CURL *curl; // cURL easy handle
  23. uint8_t *buf; // buffer
  24. off_t off0; // offset of the first byte in the buffer; the actual file offset equals off0 + p_buf
  25. int fd; // file descriptor for a normal file; <0 for a remote file
  26. int m_buf; // max buffer size; for a remote file, CURL_MAX_WRITE_SIZE*2 is recommended
  27. int l_buf; // length of the buffer; l_buf == 0 iff the input read entirely; l_buf <= m_buf
  28. int p_buf; // file position in the buffer; p_buf <= l_buf
  29. int done_reading; // true if we can read nothing from the file; buffer may not be empty even if done_reading is set
  30. int err; // error code
  31. struct curl_slist *hdr;
  32. };
  33. typedef struct {
  34. char *url, *date, *auth;
  35. } s3aux_t;
  36. int kurl_init(void) // required for SSL and win32 socket; NOT thread safe
  37. {
  38. return curl_global_init(CURL_GLOBAL_DEFAULT);
  39. }
  40. void kurl_destroy(void)
  41. {
  42. curl_global_cleanup();
  43. }
  44. static int prepare(kurl_t *ku, int do_seek)
  45. {
  46. if (kurl_isfile(ku)) {
  47. if (do_seek && lseek(ku->fd, ku->off0, SEEK_SET) != ku->off0)
  48. return -1;
  49. } else { // FIXME: for S3, we need to re-authorize
  50. int rc;
  51. rc = curl_multi_remove_handle(ku->multi, ku->curl);
  52. rc = curl_easy_setopt(ku->curl, CURLOPT_RESUME_FROM, ku->off0);
  53. rc = curl_multi_add_handle(ku->multi, ku->curl);
  54. }
  55. ku->p_buf = ku->l_buf = 0; // empty the buffer
  56. return 0;
  57. }
  58. static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) // callback required by cURL
  59. {
  60. kurl_t *ku = (kurl_t*)data;
  61. ssize_t nbytes = size * nmemb;
  62. if (nbytes + ku->l_buf > ku->m_buf)
  63. return CURL_WRITEFUNC_PAUSE;
  64. memcpy(ku->buf + ku->l_buf, ptr, nbytes);
  65. ku->l_buf += nbytes;
  66. return nbytes;
  67. }
  68. static int fill_buffer(kurl_t *ku) // fill the buffer
  69. {
  70. assert(ku->p_buf == ku->l_buf); // buffer is always used up when fill_buffer() is called; otherwise a bug
  71. ku->off0 += ku->l_buf;
  72. ku->p_buf = ku->l_buf = 0;
  73. if (ku->done_reading) return 0;
  74. if (kurl_isfile(ku)) {
  75. // The following block is equivalent to "ku->l_buf = read(ku->fd, ku->buf, ku->m_buf)" on Mac.
  76. // On Linux, the man page does not specify whether read() guarantees to read ku->m_buf bytes
  77. // even if ->fd references a normal file with sufficient remaining bytes.
  78. while (ku->l_buf < ku->m_buf) {
  79. int l;
  80. l = read(ku->fd, ku->buf + ku->l_buf, ku->m_buf - ku->l_buf);
  81. if (l == 0) break;
  82. ku->l_buf += l;
  83. }
  84. if (ku->l_buf < ku->m_buf) ku->done_reading = 1;
  85. } else {
  86. int n_running, rc;
  87. fd_set fdr, fdw, fde;
  88. do {
  89. int maxfd = -1;
  90. long curl_to = -1;
  91. struct timeval to;
  92. // the following is adaped from docs/examples/fopen.c
  93. to.tv_sec = 10, to.tv_usec = 0; // 10 seconds
  94. curl_multi_timeout(ku->multi, &curl_to);
  95. if (curl_to >= 0) {
  96. to.tv_sec = curl_to / 1000;
  97. if (to.tv_sec > 1) to.tv_sec = 1;
  98. else to.tv_usec = (curl_to % 1000) * 1000;
  99. }
  100. FD_ZERO(&fdr); FD_ZERO(&fdw); FD_ZERO(&fde);
  101. curl_multi_fdset(ku->multi, &fdr, &fdw, &fde, &maxfd); // FIXME: check return code
  102. if (maxfd >= 0 && (rc = select(maxfd+1, &fdr, &fdw, &fde, &to)) < 0) break;
  103. if (maxfd < 0) { // check curl_multi_fdset.3 about why we wait for 100ms here
  104. struct timespec req, rem;
  105. req.tv_sec = 0; req.tv_nsec = 100000000; // this is 100ms
  106. nanosleep(&req, &rem);
  107. }
  108. curl_easy_pause(ku->curl, CURLPAUSE_CONT);
  109. rc = curl_multi_perform(ku->multi, &n_running); // FIXME: check return code
  110. } while (n_running && ku->l_buf < ku->m_buf - CURL_MAX_WRITE_SIZE);
  111. if (ku->l_buf < ku->m_buf - CURL_MAX_WRITE_SIZE) ku->done_reading = 1;
  112. }
  113. return ku->l_buf;
  114. }
  115. int kurl_close(kurl_t *ku)
  116. {
  117. if (ku == 0) return 0;
  118. if (ku->fd < 0) {
  119. curl_multi_remove_handle(ku->multi, ku->curl);
  120. curl_easy_cleanup(ku->curl);
  121. curl_multi_cleanup(ku->multi);
  122. if (ku->hdr) curl_slist_free_all(ku->hdr);
  123. } else close(ku->fd);
  124. free(ku->buf);
  125. free(ku);
  126. return 0;
  127. }
  128. kurl_t *kurl_open(const char *url, kurl_opt_t *opt)
  129. {
  130. extern s3aux_t s3_parse(const char *url, const char *_id, const char *_secret, const char *fn);
  131. const char *p, *q;
  132. kurl_t *ku;
  133. int fd = -1, is_file = 1, failed = 0;
  134. p = strstr(url, "://");
  135. if (p && *p) {
  136. for (q = url; q != p; ++q)
  137. if (!isalnum(*q)) break;
  138. if (q == p) is_file = 0;
  139. }
  140. if (is_file && (fd = open(url, O_RDONLY)) < 0) return 0;
  141. ku = (kurl_t*)calloc(1, sizeof(kurl_t));
  142. ku->fd = is_file? fd : -1;
  143. if (!kurl_isfile(ku)) {
  144. ku->multi = curl_multi_init();
  145. ku->curl = curl_easy_init();
  146. if (strstr(url, "s3://") == url) {
  147. s3aux_t a;
  148. a = s3_parse(url, (opt? opt->s3keyid : 0), (opt? opt->s3secretkey : 0), (opt? opt->s3key_fn : 0));
  149. if (a.url == 0 || a.date == 0 || a.auth == 0) {
  150. kurl_close(ku);
  151. return 0;
  152. }
  153. ku->hdr = curl_slist_append(ku->hdr, a.date);
  154. ku->hdr = curl_slist_append(ku->hdr, a.auth);
  155. curl_easy_setopt(ku->curl, CURLOPT_URL, a.url);
  156. curl_easy_setopt(ku->curl, CURLOPT_HTTPHEADER, ku->hdr);
  157. free(a.date); free(a.auth); free(a.url);
  158. } else curl_easy_setopt(ku->curl, CURLOPT_URL, url);
  159. curl_easy_setopt(ku->curl, CURLOPT_WRITEDATA, ku);
  160. curl_easy_setopt(ku->curl, CURLOPT_VERBOSE, 0L);
  161. curl_easy_setopt(ku->curl, CURLOPT_NOSIGNAL, 1L);
  162. curl_easy_setopt(ku->curl, CURLOPT_WRITEFUNCTION, write_cb);
  163. curl_easy_setopt(ku->curl, CURLOPT_SSL_VERIFYPEER, 0L);
  164. curl_easy_setopt(ku->curl, CURLOPT_SSL_VERIFYHOST, 0L);
  165. curl_easy_setopt(ku->curl, CURLOPT_FOLLOWLOCATION, 1L);
  166. }
  167. ku->m_buf = KU_DEF_BUFLEN;
  168. if (!kurl_isfile(ku) && ku->m_buf < CURL_MAX_WRITE_SIZE * 2)
  169. ku->m_buf = CURL_MAX_WRITE_SIZE * 2; // for remote files, the buffer set to 2*CURL_MAX_WRITE_SIZE
  170. ku->buf = (uint8_t*)calloc(ku->m_buf, 1);
  171. if (kurl_isfile(ku)) failed = (fill_buffer(ku) <= 0);
  172. else failed = (prepare(ku, 0) < 0 || fill_buffer(ku) <= 0);
  173. if (failed) {
  174. kurl_close(ku);
  175. return 0;
  176. }
  177. return ku;
  178. }
  179. kurl_t *kurl_dopen(int fd)
  180. {
  181. kurl_t *ku;
  182. ku = (kurl_t*)calloc(1, sizeof(kurl_t));
  183. ku->fd = fd;
  184. ku->m_buf = KU_DEF_BUFLEN;
  185. ku->buf = (uint8_t*)calloc(ku->m_buf, 1);
  186. if (prepare(ku, 0) < 0 || fill_buffer(ku) <= 0) {
  187. kurl_close(ku);
  188. return 0;
  189. }
  190. return ku;
  191. }
  192. int kurl_buflen(kurl_t *ku, int len)
  193. {
  194. if (len <= 0 || len < ku->l_buf) return ku->m_buf;
  195. if (!kurl_isfile(ku) && len < CURL_MAX_WRITE_SIZE * 2) return ku->m_buf;
  196. ku->m_buf = len;
  197. kroundup32(ku->m_buf);
  198. ku->buf = (uint8_t*)realloc(ku->buf, ku->m_buf);
  199. return ku->m_buf;
  200. }
  201. ssize_t kurl_read(kurl_t *ku, void *buf, size_t nbytes)
  202. {
  203. ssize_t rest = nbytes;
  204. if (ku->l_buf == 0) return 0; // end-of-file
  205. while (rest) {
  206. if (ku->l_buf - ku->p_buf >= rest) {
  207. if (buf) memcpy((uint8_t*)buf + (nbytes - rest), ku->buf + ku->p_buf, rest);
  208. ku->p_buf += rest;
  209. rest = 0;
  210. } else {
  211. int ret;
  212. if (buf && ku->l_buf > ku->p_buf)
  213. memcpy((uint8_t*)buf + (nbytes - rest), ku->buf + ku->p_buf, ku->l_buf - ku->p_buf);
  214. rest -= ku->l_buf - ku->p_buf;
  215. ku->p_buf = ku->l_buf;
  216. ret = fill_buffer(ku);
  217. if (ret <= 0) break;
  218. }
  219. }
  220. return nbytes - rest;
  221. }
  222. off_t kurl_seek(kurl_t *ku, off_t offset, int whence) // FIXME: sometimes when seek() fails, read() will fail as well.
  223. {
  224. off_t new_off = -1, cur_off;
  225. int failed = 0, seek_end = 0;
  226. if (ku == 0) return -1;
  227. cur_off = ku->off0 + ku->p_buf;
  228. if (whence == SEEK_SET) new_off = offset;
  229. else if (whence == SEEK_CUR) new_off += cur_off + offset;
  230. else if (whence == SEEK_END && kurl_isfile(ku)) new_off = lseek(ku->fd, offset, SEEK_END), seek_end = 1;
  231. else { // not supported whence
  232. ku->err = KURL_INV_WHENCE;
  233. return -1;
  234. }
  235. if (new_off < 0) { // negtive absolute offset
  236. ku->err = KURL_SEEK_OUT;
  237. return -1;
  238. }
  239. if (!seek_end && new_off >= cur_off && new_off - cur_off + ku->p_buf < ku->l_buf) {
  240. ku->p_buf += new_off - cur_off;
  241. return ku->off0 + ku->p_buf;
  242. }
  243. if (seek_end || new_off < cur_off || new_off - cur_off > KU_MAX_SKIP) { // if jump is large, do actual seek
  244. ku->off0 = new_off;
  245. ku->done_reading = 0;
  246. if (prepare(ku, 1) < 0 || fill_buffer(ku) <= 0) failed = 1;
  247. } else { // if jump is small, read through
  248. off_t r;
  249. r = kurl_read(ku, 0, new_off - cur_off);
  250. if (r + cur_off != new_off) failed = 1; // out of range
  251. }
  252. if (failed) ku->err = KURL_SEEK_OUT, ku->l_buf = ku->p_buf = 0, new_off = -1;
  253. return new_off;
  254. }
  255. off_t kurl_tell(const kurl_t *ku)
  256. {
  257. if (ku == 0) return -1;
  258. return ku->off0 + ku->p_buf;
  259. }
  260. int kurl_eof(const kurl_t *ku)
  261. {
  262. if (ku == 0) return 1;
  263. return (ku->l_buf == 0); // unless file end, buffer should never be empty
  264. }
  265. int kurl_fileno(const kurl_t *ku)
  266. {
  267. if (ku == 0) return -1;
  268. return ku->fd;
  269. }
  270. int kurl_error(const kurl_t *ku)
  271. {
  272. if (ku == 0) return KURL_NULL;
  273. return ku->err;
  274. }
  275. /*****************
  276. *** HMAC-SHA1 ***
  277. *****************/
  278. /* This code is public-domain - it is based on libcrypt placed in the public domain by Wei Dai and other contributors. */
  279. #define HASH_LENGTH 20
  280. #define BLOCK_LENGTH 64
  281. typedef struct sha1nfo {
  282. union { uint8_t b[BLOCK_LENGTH]; uint32_t w[BLOCK_LENGTH/4]; } buf;
  283. uint8_t bufOffset;
  284. union { uint8_t b[HASH_LENGTH]; uint32_t w[HASH_LENGTH/4]; } state;
  285. uint32_t byteCount;
  286. uint8_t keyBuffer[BLOCK_LENGTH];
  287. uint8_t innerHash[HASH_LENGTH];
  288. } sha1nfo;
  289. void sha1_init(sha1nfo *s)
  290. {
  291. const uint8_t table[] = { 0x01,0x23,0x45,0x67, 0x89,0xab,0xcd,0xef, 0xfe,0xdc,0xba,0x98, 0x76,0x54,0x32,0x10, 0xf0,0xe1,0xd2,0xc3 };
  292. memcpy(s->state.b, table, HASH_LENGTH);
  293. s->byteCount = 0;
  294. s->bufOffset = 0;
  295. }
  296. #define rol32(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
  297. static void sha1_hashBlock(sha1nfo *s)
  298. {
  299. uint32_t i, t, a = s->state.w[0], b = s->state.w[1], c = s->state.w[2], d = s->state.w[3], e = s->state.w[4];
  300. for (i = 0; i < 80; i++) {
  301. if (i >= 16) {
  302. t = s->buf.w[(i+13)&15] ^ s->buf.w[(i+8)&15] ^ s->buf.w[(i+2)&15] ^ s->buf.w[i&15];
  303. s->buf.w[i&15] = rol32(t, 1);
  304. }
  305. if (i < 20) t = 0x5a827999 + (d ^ (b & (c ^ d)));
  306. else if (i < 40) t = 0x6ed9eba1 + (b ^ c ^ d);
  307. else if (i < 60) t = 0x8f1bbcdc + ((b & c) | (d & (b | c)));
  308. else t = 0xca62c1d6 + (b ^ c ^ d);
  309. t += rol32(a, 5) + e + s->buf.w[i&15];
  310. e = d; d = c; c = rol32(b, 30); b = a; a = t;
  311. }
  312. s->state.w[0] += a; s->state.w[1] += b; s->state.w[2] += c; s->state.w[3] += d; s->state.w[4] += e;
  313. }
  314. static inline void sha1_add(sha1nfo *s, uint8_t data)
  315. {
  316. s->buf.b[s->bufOffset ^ 3] = data;
  317. if (++s->bufOffset == BLOCK_LENGTH) {
  318. sha1_hashBlock(s);
  319. s->bufOffset = 0;
  320. }
  321. }
  322. void sha1_write1(sha1nfo *s, uint8_t data)
  323. {
  324. ++s->byteCount;
  325. sha1_add(s, data);
  326. }
  327. void sha1_write(sha1nfo *s, const char *data, size_t len)
  328. {
  329. while (len--) sha1_write1(s, (uint8_t)*data++);
  330. }
  331. const uint8_t *sha1_final(sha1nfo *s)
  332. {
  333. int i;
  334. sha1_add(s, 0x80);
  335. while (s->bufOffset != 56) sha1_add(s, 0);
  336. sha1_add(s, 0);
  337. sha1_add(s, 0);
  338. sha1_add(s, 0);
  339. sha1_add(s, s->byteCount >> 29);
  340. sha1_add(s, s->byteCount >> 21);
  341. sha1_add(s, s->byteCount >> 13);
  342. sha1_add(s, s->byteCount >> 5);
  343. sha1_add(s, s->byteCount << 3);
  344. for (i = 0; i < 5; ++i) {
  345. uint32_t a = s->state.w[i];
  346. s->state.w[i] = a<<24 | (a<<8&0x00ff0000) | (a>>8&0x0000ff00) | a>>24;
  347. }
  348. return s->state.b;
  349. }
  350. #define HMAC_IPAD 0x36
  351. #define HMAC_OPAD 0x5c
  352. void sha1_init_hmac(sha1nfo *s, const uint8_t* key, int l_key)
  353. {
  354. uint8_t i;
  355. memset(s->keyBuffer, 0, BLOCK_LENGTH);
  356. if (l_key > BLOCK_LENGTH) {
  357. sha1_init(s);
  358. while (l_key--) sha1_write1(s, *key++);
  359. memcpy(s->keyBuffer, sha1_final(s), HASH_LENGTH);
  360. } else memcpy(s->keyBuffer, key, l_key);
  361. sha1_init(s);
  362. for (i = 0; i < BLOCK_LENGTH; ++i)
  363. sha1_write1(s, s->keyBuffer[i] ^ HMAC_IPAD);
  364. }
  365. const uint8_t *sha1_final_hmac(sha1nfo *s)
  366. {
  367. uint8_t i;
  368. memcpy(s->innerHash, sha1_final(s), HASH_LENGTH);
  369. sha1_init(s);
  370. for (i = 0; i < BLOCK_LENGTH; ++i) sha1_write1(s, s->keyBuffer[i] ^ HMAC_OPAD);
  371. for (i = 0; i < HASH_LENGTH; ++i) sha1_write1(s, s->innerHash[i]);
  372. return sha1_final(s);
  373. }
  374. /*******************
  375. *** S3 protocol ***
  376. *******************/
  377. #include <time.h>
  378. #include <ctype.h>
  379. static void s3_sign(const char *key, const char *data, char out[29])
  380. {
  381. const char *b64tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  382. const uint8_t *digest;
  383. int i, j, rest;
  384. sha1nfo s;
  385. sha1_init_hmac(&s, (uint8_t*)key, strlen(key));
  386. sha1_write(&s, data, strlen(data));
  387. digest = sha1_final_hmac(&s);
  388. for (j = i = 0, rest = 8; i < 20; ++j) { // base64 encoding
  389. if (rest <= 6) {
  390. int next = i < 19? digest[i+1] : 0;
  391. out[j] = b64tab[(int)(digest[i] << (6-rest) & 0x3f) | next >> (rest+2)], ++i, rest += 2;
  392. } else out[j] = b64tab[(int)digest[i] >> (rest-6) & 0x3f], rest -= 6;
  393. }
  394. out[j++] = '='; out[j] = 0; // SHA1 digest always has 160 bits, or 20 bytes. We need one '=' at the end.
  395. }
  396. static char *s3_read_awssecret(const char *fn)
  397. {
  398. char *p, *secret, buf[128], *path;
  399. FILE *fp;
  400. int l;
  401. if (fn == 0) {
  402. char *home;
  403. home = getenv("HOME");
  404. if (home == 0) return 0;
  405. l = strlen(home) + 12;
  406. path = (char*)malloc(strlen(home) + 12);
  407. strcat(strcpy(path, home), "/.awssecret");
  408. } else path = (char*)fn;
  409. fp = fopen(path, "r");
  410. if (path != fn) free(path);
  411. if (fp == 0) return 0;
  412. l = fread(buf, 1, 127, fp);
  413. fclose(fp);
  414. buf[l] = 0;
  415. for (p = buf; *p != 0 && *p != '\n'; ++p);
  416. if (*p == 0) return 0;
  417. *p = 0; secret = p + 1;
  418. for (++p; *p != 0 && *p != '\n'; ++p);
  419. *p = 0;
  420. l = p - buf + 1;
  421. p = (char*)malloc(l);
  422. memcpy(p, buf, l);
  423. return p;
  424. }
  425. typedef struct { int l, m; char *s; } kstring_t;
  426. static inline int kputsn(const char *p, int l, kstring_t *s)
  427. {
  428. if (s->l + l + 1 >= s->m) {
  429. s->m = s->l + l + 2;
  430. kroundup32(s->m);
  431. s->s = (char*)realloc(s->s, s->m);
  432. }
  433. memcpy(s->s + s->l, p, l);
  434. s->l += l;
  435. s->s[s->l] = 0;
  436. return l;
  437. }
  438. s3aux_t s3_parse(const char *url, const char *_id, const char *_secret, const char *fn_secret)
  439. {
  440. const char *id, *secret, *bucket, *obj;
  441. char *id_secret = 0, date[64], sig[29];
  442. time_t t;
  443. struct tm tmt;
  444. s3aux_t a = {0,0};
  445. kstring_t str = {0,0,0};
  446. // parse URL
  447. if (strstr(url, "s3://") != url) return a;
  448. bucket = url + 5;
  449. for (obj = bucket; *obj && *obj != '/'; ++obj);
  450. if (*obj == 0) return a; // no object
  451. // acquire AWS credential and time
  452. if (_id == 0 || _secret == 0) {
  453. id_secret = s3_read_awssecret(fn_secret);
  454. if (id_secret == 0) return a; // fail to read the AWS credential
  455. id = id_secret;
  456. secret = id_secret + strlen(id) + 1;
  457. } else id = _id, secret = _secret;
  458. // compose URL for curl
  459. kputsn("https://", 8, &str);
  460. kputsn(bucket, obj - bucket, &str);
  461. kputsn(".s3.amazonaws.com", 17, &str);
  462. kputsn(obj, strlen(obj), &str);
  463. a.url = str.s;
  464. // compose the Date line
  465. str.l = str.m = 0; str.s = 0;
  466. t = time(0);
  467. strftime(date, 64, "%a, %d %b %Y %H:%M:%S +0000", gmtime_r(&t, &tmt));
  468. kputsn("Date: ", 6, &str);
  469. kputsn(date, strlen(date), &str);
  470. a.date = str.s;
  471. // compose the string to sign and sign it
  472. str.l = str.m = 0; str.s = 0;
  473. kputsn("GET\n\n\n", 6, &str);
  474. kputsn(date, strlen(date), &str);
  475. kputsn("\n", 1, &str);
  476. kputsn(bucket-1, strlen(bucket-1), &str);
  477. s3_sign(secret, str.s, sig);
  478. // compose the Authorization line
  479. str.l = 0;
  480. kputsn("Authorization: AWS ", 19, &str);
  481. kputsn(id, strlen(id), &str);
  482. kputsn(":", 1, &str);
  483. kputsn(sig, strlen(sig), &str);
  484. a.auth = str.s;
  485. // printf("curl -H '%s' -H '%s' %s\n", a.date, a.auth, a.url);
  486. return a;
  487. }
  488. /*********************
  489. *** Main function ***
  490. *********************/
  491. #ifdef KURL_MAIN
  492. int main(int argc, char *argv[])
  493. {
  494. kurl_t *f;
  495. int c, l, l_buf = 0x10000;
  496. off_t start = 0, rest = -1;
  497. uint8_t *buf;
  498. char *p;
  499. kurl_opt_t opt;
  500. memset(&opt, 0, sizeof(kurl_opt_t));
  501. while ((c = getopt(argc, argv, "c:l:a:")) >= 0) {
  502. if (c == 'c') start = strtol(optarg, &p, 0);
  503. else if (c == 'l') rest = strtol(optarg, &p, 0);
  504. else if (c == 'a') opt.s3key_fn = optarg;
  505. }
  506. if (optind == argc) {
  507. fprintf(stderr, "Usage: kurl [-c start] [-l length] <url>\n");
  508. return 1;
  509. }
  510. kurl_init();
  511. f = kurl_open(argv[optind], &opt);
  512. if (f == 0) {
  513. fprintf(stderr, "ERROR: fail to open URL\n");
  514. return 2;
  515. }
  516. if (start > 0) {
  517. if (kurl_seek(f, start, SEEK_SET) < 0) {
  518. kurl_close(f);
  519. fprintf(stderr, "ERROR: fail to seek\n");
  520. return 3;
  521. }
  522. }
  523. buf = (uint8_t*)calloc(l_buf, 1);
  524. while (rest != 0) {
  525. int to_read = rest > 0 && rest < l_buf? rest : l_buf;
  526. l = kurl_read(f, buf, to_read);
  527. if (l == 0) break;
  528. fwrite(buf, 1, l, stdout);
  529. rest -= l;
  530. }
  531. free(buf);
  532. kurl_close(f);
  533. kurl_destroy();
  534. return 0;
  535. }
  536. #endif