{"action":"created","comment":{"url":"https://api.github.com/repos/sustrik/libdill/pulls/comments/225800862","pull_request_review_id":165479875,"id":225800862,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyNTgwMDg2Mg==","diff_hunk":"@@ -0,0 +1,337 @@\n+/*\n+\n+  Copyright (c) 2018 Joseph deBlaquiere, Martin Sustrik\n+\n+  Permission is hereby granted, free of charge, to any person obtaining a copy\n+  of this software and associated documentation files (the \"Software\"),\n+  to deal in the Software without restriction, including without limitation\n+  the rights to use, copy, modify, merge, publish, distribute, sublicense,\n+  and/or sell copies of the Software, and to permit persons to whom\n+  the Software is furnished to do so, subject to the following conditions:\n+\n+  The above copyright notice and this permission notice shall be included\n+  in all copies or substantial portions of the Software.\n+\n+  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n+  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+  IN THE SOFTWARE.\n+\n+*/\n+\n+#include <arpa/inet.h>\n+#include <errno.h>\n+#include <stddef.h>\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#define DILL_DISABLE_RAW_NAMES\n+#include \"libdillimpl.h\"\n+#include \"utils.h\"\n+\n+dill_unique_id(dill_socks5_type);\n+\n+static void *dill_socks5_hquery(struct dill_hvfs *hvfs, const void *type);\n+static void dill_socks5_hclose(struct dill_hvfs *hvfs);\n+static int dill_socks5_bsendl(struct dill_bsock_vfs *bvfs,\n+    struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);\n+static int dill_socks5_brecvl(struct dill_bsock_vfs *bvfs,\n+    struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);\n+\n+// SOCKS5 as defined in RFC 1928\n+\n+struct dill_socks5_sock {\n+    struct dill_hvfs hvfs;\n+    struct dill_bsock_vfs bvfs;\n+    /* Underlying SUFFIX socket. */\n+    int u;\n+    unsigned int mem : 1;\n+    unsigned int server : 1;\n+    unsigned int connect : 1;\n+    //struct dill_suffix_storage suffix_mem;\n+    //struct dill_term_storage term_mem;\n+    char rxbuf[1024];\n+};\n+\n+DILL_CHECK_STORAGE(dill_socks5_sock, dill_socks5_storage)\n+\n+static void *dill_socks5_hquery(struct dill_hvfs *hvfs, const void *type) {\n+    struct dill_socks5_sock *obj = (struct dill_socks5_sock*)hvfs;\n+    if(type == dill_socks5_type) return obj;\n+    if(type == dill_bsock_type) return &obj->bvfs;\n+    errno = ENOTSUP;\n+    return NULL;\n+}\n+\n+static int dill_socks5_clientauth(int s, const char *username, const char *password,\n+      int64_t deadline) {\n+    // ensure socket is attached as socks5 \n+    struct dill_socks5_sock *obj = dill_hquery(s, dill_socks5_type);\n+    if(dill_slow(!obj)) return -1;\n+    // validate input, specifically user and pass, which may be NULL\n+    if (username) {\n+        if (strlen(username) > 255) {errno = EINVAL; return -1;}\n+    }\n+    if (password) {\n+        if (strlen(password) > 255) {errno = EINVAL; return -1;}\n+    }\n+    // version identifier/method selection request\n+    // client passes list of desired auth methods\n+    uint8_t vims[4];\n+    size_t vims_len;\n+    vims[0] = 0x05; // VER = 5\n+    vims[2] = 0x00; // METHOD = NO AUTH REQ'D\n+    vims[3] = 0x02; // METHOD = USERNAME/PASSWORD\n+    // TODO: Add support for GSSAPI Auth\n+    // if we don't have both username and password only request NO AUTH\n+    if ((!username) || (!password)) {\n+        vims[1] = 0x01; // NMETHODS = 1 (NO AUTH)\n+        vims_len = 3;\n+    } else {\n+        vims[1] = 0x02; // NMETHODS = 2 (NO AUTH or USER/PASS)\n+        vims_len = 4;\n+    }\n+    //for (int i = 0; i < vims_len; i++) {printf(\"%02X \", vims[i]);} printf(\">\\n\");\n+    int err = dill_bsend(obj->u, (void *)vims, vims_len, deadline);\n+    if (dill_slow(err)) {return -1;}\n+    // version identifier/method response\n+    // server responds with chosen auth method (or error)\n+    uint8_t vimr[2];\n+    err = dill_brecv(obj->u, (void *)vimr, 2, deadline);\n+    if (dill_slow(err)) {return -1;}\n+    //for (int i = 0; i < 2; i++) {printf(\"%02X \", vimr[i]);} printf(\"<\\n\");\n+    // validate VER in response\n+    if (dill_slow(vimr[0] != 0x05)) {errno = EPROTO; return -1;}\n+    // if proxy accepted NO AUTH REQ'D, we're done\n+    if (vimr[1] == 0x00) {return 0;}\n+    if (vimr[1] == 0x02) {\n+        dill_assert(!!username);\n+        dill_assert(!!password);\n+        // USER/PASS AUTH - per RFC 1929\n+        // max USER, PASS is 255 bytes (plus 3 bytes for VER, ULEN, PLEN)\n+        uint8_t upauth[513];\n+        // previously range checked, safe to cast to uint8_t\n+        uint8_t ulen = (uint8_t)strlen(username);\n+        uint8_t plen = (uint8_t)strlen(password);\n+        upauth[0] = 0x01; // VER = 1\n+        upauth[1] = ulen; // ULEN\n+        strncpy((char *)(upauth + 2), username, ulen); // UNAME\n+        upauth[2 + ulen] = plen; // PLEN\n+        strncpy((char *)(upauth + 3 + ulen), password, plen); // PASSWD\n+        //for (int i = 0; i < 3+ulen+plen; i++) {printf(\"%02X \", upauth[i]);} printf(\">\\n\");\n+        err = dill_bsend(obj->u, (void *)upauth, 3 + ulen + plen, deadline);\n+        if (dill_slow(err)) {return -1;}\n+        uint8_t upauthr[2];\n+        err = dill_brecv(obj->u, (void *)upauthr, 2, deadline);\n+        if (dill_slow(err)) {return -1;}\n+        //for (int i = 0; i < 2; i++) {printf(\"%02X \", upauthr[i]);} printf(\"<\\n\");\n+        if (dill_slow(upauthr[0] != 0x01)) {errno = EPROTO; return -1;}\n+        if (dill_slow(upauthr[1] != 0x00)) {errno = EACCES; return -1;}\n+        return 0;\n+    }\n+    if (vimr[1] == 0xFF) {errno = EACCES; return -1;}\n+    dill_assert((vimr[1] == 0x00) || (vimr[1] == 0x02) || (vimr[1] == 0xFF));\n+    return -1;\n+}\n+\n+int dill_socks5_attach_client_mem(int s, const char *username,\n+      const char *password, struct dill_socks5_storage *mem,\n+      int64_t deadline) {\n+    int err;\n+    struct dill_socks5_sock *obj = (struct dill_socks5_sock*)mem;\n+    if(dill_slow(!mem)) {err = EINVAL; goto error;}\n+    /* Check whether underlying socket is a bytestream. */\n+    if(dill_slow(!dill_hquery(s, dill_bsock_type))) {err = errno; goto error;}\n+    /* Take the ownership of the underlying socket. */\n+    s = dill_hown(s);\n+    if(dill_slow(s < 0)) {err = errno; goto error;}\n+    /* Create the object. */\n+    obj->hvfs.query = dill_socks5_hquery;\n+    obj->hvfs.close = dill_socks5_hclose;\n+    obj->bvfs.bsendl = dill_socks5_bsendl;\n+    obj->bvfs.brecvl = dill_socks5_brecvl;\n+    obj->u = s;\n+    obj->mem = 1;\n+    obj->server = 0;\n+    obj->connect = 0;\n+    /* Create the handle. */\n+    int h = dill_hmake(&obj->hvfs);\n+    if(dill_slow(h < 0)) {err = errno; goto error;}\n+    err = dill_socks5_clientauth(h, username, password, deadline);\n+    if (err) {s = h; err = errno; goto error;}\n+    return h;\n+error:\n+    if(s >= 0) dill_hclose(s);\n+    errno = err;\n+    return -1;\n+}\n+\n+int dill_socks5_attach_client(int s, const char *username,\n+      const char *password, int64_t deadline) {\n+    int err;\n+    struct dill_socks5_sock *obj = malloc(sizeof(struct dill_socks5_sock));\n+    if(dill_slow(!obj)) {err = ENOMEM; goto error1;}\n+    s = dill_socks5_attach_client_mem(s, username, password,\n+        (struct dill_socks5_storage*)obj, deadline);\n+    if(dill_slow(s < 0)) {err = errno; goto error2;}\n+    obj->mem = 0;\n+    return s;\n+error2:\n+    free(obj);\n+error1:\n+    if(s >= 0) dill_hclose(s);\n+    errno = err;\n+    return -1;\n+}\n+\n+int dill_socks5_connect(int s, char *addr, int port, int64_t deadline) {\n+    // validate socket type\n+    struct dill_socks5_sock *obj = dill_hquery(s, dill_socks5_type);\n+    if(dill_slow(!obj)) return -1;\n+    if(dill_slow(obj->server)) {errno = EPROTO; return -1;}\n+    if(dill_slow(obj->connect)) {errno = EISCONN; return -1;}\n+    // validate input\n+    if(dill_slow(!addr)) {errno = EINVAL; return -1;}\n+    if(dill_slow(strlen(addr) > 255)) {errno = EINVAL; return -1;}\n+    if((port <= 0) || (port>65535)) {errno = EINVAL; return -1;}\n+    // largest possible connect request = 255 chars for name + 7 bytes for\n+    // VER, CMD, RSV, ATYP, ALEN, PORT[2]\n+    uint8_t conn[262];\n+    size_t conn_len;\n+    conn[0] = 0x05; // VER = 5\n+    conn[1] = 0x01; // CMD = CONNECT\n+    conn[2] = 0x00; // RSV\n+    struct in_addr ina4;\n+    struct in6_addr ina6;\n+    if (inet_pton(AF_INET, addr, &ina4) == 1) {\n+        // IPv4 Address, now stored in ina4, Network Byte Order\n+        conn[3] = 0x01; // ATYP = IPV4\n+        memcpy((void *)(conn + 4), (void *)&ina4, 4);\n+        conn[8] = port >> 8;\n+        conn[9] = port & 0xFF;\n+        conn_len = 10;\n+    } else if (inet_pton(AF_INET6, addr, &ina6) == 1) {\n+        // IPv6 Address, now stored in ina6, Network Byte Order\n+        conn[3] = 0x04; // ATYP = IPV6\n+        memcpy((void *)(conn + 4), (void *)&ina6, 16);\n+        conn[20] = port >> 8;\n+        conn[21] = port & 0xFF;\n+        conn_len = 22;\n+   } else {\n+        // assume a resolvable domain name\n+        // previously validate to be 0 < x <= 255 bytes\n+        uint8_t alen = (uint8_t)strlen(addr);\n+        conn[3] = 0x03; // ATYP = DOMAINNAME\n+        conn[4] = alen;\n+        memcpy((void *)(conn+ 5), (void *)addr, alen);\n+        conn[5+alen] = port >> 8;\n+        conn[6+alen] = port & 0xFF;\n+        conn_len = 7 + alen;\n+    }\n+    int err = dill_bsend(obj->u, conn, conn_len, deadline);\n+    if (dill_slow(err)) {return -1;}\n+    // reply has the same form/length limit, reuse request as reply buffer\n+    // read first 5 octets (through ATYP) to determine how much more to read\n+    err = dill_brecv(obj->u, conn, 5, deadline);\n+    if (dill_slow(err)) {return -1;}\n+    switch (conn[3]) {\n+        case 0x01:\n+            // IPv4, already read 1 bytes, read remaining 3 and port\n+            err = dill_brecv(obj->u, conn + 5, 5, deadline);\n+            if (dill_slow(err)) {return -1;}\n+            break;\n+        case 0x03:\n+            // IPv4, already read 1 bytes, read remaining 15 and port\n+            err = dill_brecv(obj->u, conn + 5, 17, deadline);\n+            if (dill_slow(err)) {return -1;}\n+            break;\n+        case 0x04:\n+            // IPv4, already read 1 bytes, read ALEN-1 add + 2 for port\n+            err = dill_brecv(obj->u, conn + 5, conn[4] + 1, deadline);\n+            if (dill_slow(err)) {return -1;}\n+            break;\n+        default:\n+            errno = EPROTO;\n+            return -1;\n+    }\n+    if (conn[1] == 0x00) {\n+        // success - from here on should treat as if attached via tcp_connect\n+        obj->connect = 1;\n+        return 0;\n+    }\n+    // failed, it's just a matter of deciding which error code to return\n+    switch (conn[1]) {\n+        case 0x01:","path":"socks5.c","position":269,"original_position":269,"commit_id":"ca2e4396b81b07d9320f23b8622a1c9f5ca7c91f","original_commit_id":"ca2e4396b81b07d9320f23b8622a1c9f5ca7c91f","user":{"login":"sustrik","id":305718,"node_id":"MDQ6VXNlcjMwNTcxOA==","avatar_url":"https://avatars2.githubusercontent.com/u/305718?v=4","gravatar_id":"","url":"https://api.github.com/users/sustrik","html_url":"https://github.com/sustrik","followers_url":"https://api.github.com/users/sustrik/followers","following_url":"https://api.github.com/users/sustrik/following{/other_user}","gists_url":"https://api.github.com/users/sustrik/gists{/gist_id}","starred_url":"https://api.github.com/users/sustrik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sustrik/subscriptions","organizations_url":"https://api.github.com/users/sustrik/orgs","repos_url":"https://api.github.com/users/sustrik/repos","events_url":"https://api.github.com/users/sustrik/events{/privacy}","received_events_url":"https://api.github.com/users/sustrik/received_events","type":"User","site_admin":false},"body":"Possible style improvement (to reduce the number of lines):\r\n\r\n```\r\ncase 0x01: // general SOCKS server failure\r\n    errno = EIO; break;\r\n```\r\n\r\nOr maybe:\r\n\r\n```\r\n// general SOCKS server failure\r\ncase 0x01: errno = EIO; break;\r\n```","created_at":"2018-10-17T07:05:42Z","updated_at":"2018-10-17T07:05:42Z","html_url":"https://github.com/sustrik/libdill/pull/164#discussion_r225800862","pull_request_url":"https://api.github.com/repos/sustrik/libdill/pulls/164","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/comments/225800862"},"html":{"href":"https://github.com/sustrik/libdill/pull/164#discussion_r225800862"},"pull_request":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/164"}}},"pull_request":{"url":"https://api.github.com/repos/sustrik/libdill/pulls/164","id":223451737,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIzNDUxNzM3","html_url":"https://github.com/sustrik/libdill/pull/164","diff_url":"https://github.com/sustrik/libdill/pull/164.diff","patch_url":"https://github.com/sustrik/libdill/pull/164.patch","issue_url":"https://api.github.com/repos/sustrik/libdill/issues/164","number":164,"state":"open","locked":false,"title":"initial client side attach and tcp connect for socks5","user":{"login":"jadeblaquiere","id":11371619,"node_id":"MDQ6VXNlcjExMzcxNjE5","avatar_url":"https://avatars1.githubusercontent.com/u/11371619?v=4","gravatar_id":"","url":"https://api.github.com/users/jadeblaquiere","html_url":"https://github.com/jadeblaquiere","followers_url":"https://api.github.com/users/jadeblaquiere/followers","following_url":"https://api.github.com/users/jadeblaquiere/following{/other_user}","gists_url":"https://api.github.com/users/jadeblaquiere/gists{/gist_id}","starred_url":"https://api.github.com/users/jadeblaquiere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jadeblaquiere/subscriptions","organizations_url":"https://api.github.com/users/jadeblaquiere/orgs","repos_url":"https://api.github.com/users/jadeblaquiere/repos","events_url":"https://api.github.com/users/jadeblaquiere/events{/privacy}","received_events_url":"https://api.github.com/users/jadeblaquiere/received_events","type":"User","site_admin":false},"body":"While this is far from a complete work (still working on server side protocol, tests and man page entries, for instance...), I wanted to push this out for review and comment. \r\n\r\nThe client example in tutorial/sockets/step5.c does work nicely with the two different SOCKS5 implementations (microsocks and tor) that I have tested against manually. \r\n\r\nI've tried to follow style and naming but I won't be at all offended if you suggest some changes.","created_at":"2018-10-17T02:41:28Z","updated_at":"2018-10-17T07:05:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0920838353b6cb6130806188a67704d7992edfd3","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/sustrik/libdill/pulls/164/commits","review_comments_url":"https://api.github.com/repos/sustrik/libdill/pulls/164/comments","review_comment_url":"https://api.github.com/repos/sustrik/libdill/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sustrik/libdill/issues/164/comments","statuses_url":"https://api.github.com/repos/sustrik/libdill/statuses/ca2e4396b81b07d9320f23b8622a1c9f5ca7c91f","head":{"label":"jadeblaquiere:socks5","ref":"socks5","sha":"ca2e4396b81b07d9320f23b8622a1c9f5ca7c91f","user":{"login":"jadeblaquiere","id":11371619,"node_id":"MDQ6VXNlcjExMzcxNjE5","avatar_url":"https://avatars1.githubusercontent.com/u/11371619?v=4","gravatar_id":"","url":"https://api.github.com/users/jadeblaquiere","html_url":"https://github.com/jadeblaquiere","followers_url":"https://api.github.com/users/jadeblaquiere/followers","following_url":"https://api.github.com/users/jadeblaquiere/following{/other_user}","gists_url":"https://api.github.com/users/jadeblaquiere/gists{/gist_id}","starred_url":"https://api.github.com/users/jadeblaquiere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jadeblaquiere/subscriptions","organizations_url":"https://api.github.com/users/jadeblaquiere/orgs","repos_url":"https://api.github.com/users/jadeblaquiere/repos","events_url":"https://api.github.com/users/jadeblaquiere/events{/privacy}","received_events_url":"https://api.github.com/users/jadeblaquiere/received_events","type":"User","site_admin":false},"repo":{"id":151350317,"node_id":"MDEwOlJlcG9zaXRvcnkxNTEzNTAzMTc=","name":"libdill","full_name":"jadeblaquiere/libdill","private":false,"owner":{"login":"jadeblaquiere","id":11371619,"node_id":"MDQ6VXNlcjExMzcxNjE5","avatar_url":"https://avatars1.githubusercontent.com/u/11371619?v=4","gravatar_id":"","url":"https://api.github.com/users/jadeblaquiere","html_url":"https://github.com/jadeblaquiere","followers_url":"https://api.github.com/users/jadeblaquiere/followers","following_url":"https://api.github.com/users/jadeblaquiere/following{/other_user}","gists_url":"https://api.github.com/users/jadeblaquiere/gists{/gist_id}","starred_url":"https://api.github.com/users/jadeblaquiere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jadeblaquiere/subscriptions","organizations_url":"https://api.github.com/users/jadeblaquiere/orgs","repos_url":"https://api.github.com/users/jadeblaquiere/repos","events_url":"https://api.github.com/users/jadeblaquiere/events{/privacy}","received_events_url":"https://api.github.com/users/jadeblaquiere/received_events","type":"User","site_admin":false},"html_url":"https://github.com/jadeblaquiere/libdill","description":"Structured concurrency in C","fork":true,"url":"https://api.github.com/repos/jadeblaquiere/libdill","forks_url":"https://api.github.com/repos/jadeblaquiere/libdill/forks","keys_url":"https://api.github.com/repos/jadeblaquiere/libdill/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jadeblaquiere/libdill/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jadeblaquiere/libdill/teams","hooks_url":"https://api.github.com/repos/jadeblaquiere/libdill/hooks","issue_events_url":"https://api.github.com/repos/jadeblaquiere/libdill/issues/events{/number}","events_url":"https://api.github.com/repos/jadeblaquiere/libdill/events","assignees_url":"https://api.github.com/repos/jadeblaquiere/libdill/assignees{/user}","branches_url":"https://api.github.com/repos/jadeblaquiere/libdill/branches{/branch}","tags_url":"https://api.github.com/repos/jadeblaquiere/libdill/tags","blobs_url":"https://api.github.com/repos/jadeblaquiere/libdill/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jadeblaquiere/libdill/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jadeblaquiere/libdill/git/refs{/sha}","trees_url":"https://api.github.com/repos/jadeblaquiere/libdill/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jadeblaquiere/libdill/statuses/{sha}","languages_url":"https://api.github.com/repos/jadeblaquiere/libdill/languages","stargazers_url":"https://api.github.com/repos/jadeblaquiere/libdill/stargazers","contributors_url":"https://api.github.com/repos/jadeblaquiere/libdill/contributors","subscribers_url":"https://api.github.com/repos/jadeblaquiere/libdill/subscribers","subscription_url":"https://api.github.com/repos/jadeblaquiere/libdill/subscription","commits_url":"https://api.github.com/repos/jadeblaquiere/libdill/commits{/sha}","git_commits_url":"https://api.github.com/repos/jadeblaquiere/libdill/git/commits{/sha}","comments_url":"https://api.github.com/repos/jadeblaquiere/libdill/comments{/number}","issue_comment_url":"https://api.github.com/repos/jadeblaquiere/libdill/issues/comments{/number}","contents_url":"https://api.github.com/repos/jadeblaquiere/libdill/contents/{+path}","compare_url":"https://api.github.com/repos/jadeblaquiere/libdill/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jadeblaquiere/libdill/merges","archive_url":"https://api.github.com/repos/jadeblaquiere/libdill/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jadeblaquiere/libdill/downloads","issues_url":"https://api.github.com/repos/jadeblaquiere/libdill/issues{/number}","pulls_url":"https://api.github.com/repos/jadeblaquiere/libdill/pulls{/number}","milestones_url":"https://api.github.com/repos/jadeblaquiere/libdill/milestones{/number}","notifications_url":"https://api.github.com/repos/jadeblaquiere/libdill/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jadeblaquiere/libdill/labels{/name}","releases_url":"https://api.github.com/repos/jadeblaquiere/libdill/releases{/id}","deployments_url":"https://api.github.com/repos/jadeblaquiere/libdill/deployments","created_at":"2018-10-03T02:18:49Z","updated_at":"2018-10-17T01:36:23Z","pushed_at":"2018-10-17T02:29:13Z","git_url":"git://github.com/jadeblaquiere/libdill.git","ssh_url":"git@github.com:jadeblaquiere/libdill.git","clone_url":"https://github.com/jadeblaquiere/libdill.git","svn_url":"https://github.com/jadeblaquiere/libdill","homepage":"","size":18406,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"sustrik:master","ref":"master","sha":"4c97da4fe5d379cb41af4f5f32d444799f13608a","user":{"login":"sustrik","id":305718,"node_id":"MDQ6VXNlcjMwNTcxOA==","avatar_url":"https://avatars2.githubusercontent.com/u/305718?v=4","gravatar_id":"","url":"https://api.github.com/users/sustrik","html_url":"https://github.com/sustrik","followers_url":"https://api.github.com/users/sustrik/followers","following_url":"https://api.github.com/users/sustrik/following{/other_user}","gists_url":"https://api.github.com/users/sustrik/gists{/gist_id}","starred_url":"https://api.github.com/users/sustrik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sustrik/subscriptions","organizations_url":"https://api.github.com/users/sustrik/orgs","repos_url":"https://api.github.com/users/sustrik/repos","events_url":"https://api.github.com/users/sustrik/events{/privacy}","received_events_url":"https://api.github.com/users/sustrik/received_events","type":"User","site_admin":false},"repo":{"id":49762853,"node_id":"MDEwOlJlcG9zaXRvcnk0OTc2Mjg1Mw==","name":"libdill","full_name":"sustrik/libdill","private":false,"owner":{"login":"sustrik","id":305718,"node_id":"MDQ6VXNlcjMwNTcxOA==","avatar_url":"https://avatars2.githubusercontent.com/u/305718?v=4","gravatar_id":"","url":"https://api.github.com/users/sustrik","html_url":"https://github.com/sustrik","followers_url":"https://api.github.com/users/sustrik/followers","following_url":"https://api.github.com/users/sustrik/following{/other_user}","gists_url":"https://api.github.com/users/sustrik/gists{/gist_id}","starred_url":"https://api.github.com/users/sustrik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sustrik/subscriptions","organizations_url":"https://api.github.com/users/sustrik/orgs","repos_url":"https://api.github.com/users/sustrik/repos","events_url":"https://api.github.com/users/sustrik/events{/privacy}","received_events_url":"https://api.github.com/users/sustrik/received_events","type":"User","site_admin":false},"html_url":"https://github.com/sustrik/libdill","description":"Structured concurrency in C","fork":false,"url":"https://api.github.com/repos/sustrik/libdill","forks_url":"https://api.github.com/repos/sustrik/libdill/forks","keys_url":"https://api.github.com/repos/sustrik/libdill/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sustrik/libdill/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sustrik/libdill/teams","hooks_url":"https://api.github.com/repos/sustrik/libdill/hooks","issue_events_url":"https://api.github.com/repos/sustrik/libdill/issues/events{/number}","events_url":"https://api.github.com/repos/sustrik/libdill/events","assignees_url":"https://api.github.com/repos/sustrik/libdill/assignees{/user}","branches_url":"https://api.github.com/repos/sustrik/libdill/branches{/branch}","tags_url":"https://api.github.com/repos/sustrik/libdill/tags","blobs_url":"https://api.github.com/repos/sustrik/libdill/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sustrik/libdill/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sustrik/libdill/git/refs{/sha}","trees_url":"https://api.github.com/repos/sustrik/libdill/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sustrik/libdill/statuses/{sha}","languages_url":"https://api.github.com/repos/sustrik/libdill/languages","stargazers_url":"https://api.github.com/repos/sustrik/libdill/stargazers","contributors_url":"https://api.github.com/repos/sustrik/libdill/contributors","subscribers_url":"https://api.github.com/repos/sustrik/libdill/subscribers","subscription_url":"https://api.github.com/repos/sustrik/libdill/subscription","commits_url":"https://api.github.com/repos/sustrik/libdill/commits{/sha}","git_commits_url":"https://api.github.com/repos/sustrik/libdill/git/commits{/sha}","comments_url":"https://api.github.com/repos/sustrik/libdill/comments{/number}","issue_comment_url":"https://api.github.com/repos/sustrik/libdill/issues/comments{/number}","contents_url":"https://api.github.com/repos/sustrik/libdill/contents/{+path}","compare_url":"https://api.github.com/repos/sustrik/libdill/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sustrik/libdill/merges","archive_url":"https://api.github.com/repos/sustrik/libdill/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sustrik/libdill/downloads","issues_url":"https://api.github.com/repos/sustrik/libdill/issues{/number}","pulls_url":"https://api.github.com/repos/sustrik/libdill/pulls{/number}","milestones_url":"https://api.github.com/repos/sustrik/libdill/milestones{/number}","notifications_url":"https://api.github.com/repos/sustrik/libdill/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sustrik/libdill/labels{/name}","releases_url":"https://api.github.com/repos/sustrik/libdill/releases{/id}","deployments_url":"https://api.github.com/repos/sustrik/libdill/deployments","created_at":"2016-01-16T06:46:15Z","updated_at":"2018-10-17T06:13:25Z","pushed_at":"2018-10-17T06:13:23Z","git_url":"git://github.com/sustrik/libdill.git","ssh_url":"git@github.com:sustrik/libdill.git","clone_url":"https://github.com/sustrik/libdill.git","svn_url":"https://github.com/sustrik/libdill","homepage":"","size":18398,"stargazers_count":879,"watchers_count":879,"language":"C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":82,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":82,"open_issues":14,"watchers":879,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/164"},"html":{"href":"https://github.com/sustrik/libdill/pull/164"},"issue":{"href":"https://api.github.com/repos/sustrik/libdill/issues/164"},"comments":{"href":"https://api.github.com/repos/sustrik/libdill/issues/164/comments"},"review_comments":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/164/comments"},"review_comment":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sustrik/libdill/pulls/164/commits"},"statuses":{"href":"https://api.github.com/repos/sustrik/libdill/statuses/ca2e4396b81b07d9320f23b8622a1c9f5ca7c91f"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"sustrik"}}