diff --git a/src/anchor.c b/src/anchor.c index ab48cb6a..8a615657 100644 --- a/src/anchor.c +++ b/src/anchor.c @@ -1029,9 +1029,9 @@ static void tas_doc_read(getdns_context *context, tas_connection *a) else { context->trust_anchors = tas; context->trust_anchors_len = tas_len; - _getdns_context_write_priv_file( + (void) _getdns_context_write_priv_file( context, "root-anchors.xml", &a->xml); - _getdns_context_write_priv_file( + (void) _getdns_context_write_priv_file( context, "root-anchors.p7s", &p7s_bd); tas_success(context, a); return; @@ -1403,7 +1403,7 @@ static void tas_connect(getdns_context *context, tas_connection *a) GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, getdns_eventloop_event_init(&a->event, a->req->owner, NULL, tas_write_cb, tas_timeout_cb)); - DEBUG_ANCHOR("Scheduled write\n"); + DEBUG_ANCHOR("Scheduled write with event\n"); return; } else DEBUG_ANCHOR("Connect error: %s\n", strerror(errno)); @@ -1420,8 +1420,11 @@ static void tas_happy_eyeballs_cb(void *userarg) assert(dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A); if (tas_fetching(&context->aaaa)) return; - else + else { + DEBUG_ANCHOR("AAAA came too late, clearing Happy Eyeballs timer\n"); + GETDNS_CLEAR_EVENT(context->a.loop, &context->a.event); tas_connect(context, &context->a); + } } static void _tas_hostname_lookup_cb(getdns_dns_req *dnsreq) @@ -1494,7 +1497,12 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) const char *verify_CA; const char *verify_email; - if ((r = _getdns_get_tas_url_hostname(context, tas_hostname, NULL))) { + if (!_getdns_context_can_write_appdata(context)) { + DEBUG_ANCHOR("NOTICE %s(): Not fetching TA, because " + "non writeable appdata directory\n", __FUNC__); + return; + + } else if ((r = _getdns_get_tas_url_hostname(context, tas_hostname, NULL))) { DEBUG_ANCHOR("ERROR %s(): Could not get_tas_url_hostname" ": \"%s\"", __FUNC__ , getdns_get_errorstr_by_id(r)); diff --git a/src/context.c b/src/context.c index 6f57143e..89a86005 100644 --- a/src/context.c +++ b/src/context.c @@ -41,6 +41,7 @@ #include #include #include +#include #else #include #include @@ -4601,82 +4602,202 @@ getdns_context_config(getdns_context *context, const getdns_dict *config_dict) return r; } +static size_t _getdns_get_appdata(char *path) +{ + size_t len; + +#ifdef USE_WINSOCK +# define APPDATA_SUBDIR "getdns" + + if (! SUCCEEDED(SHGetFolderPath(NULL, + CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path))) + DEBUG_ANCHOR("ERROR %s(): Could not get \%AppData\% directory\n" + , __FUNC__); + + else if ((len = strlen(path)) + sizeof(APPDATA_SUBDIR) + 2 >= PATH_MAX) + DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n" + , __FUNC__); +#else +# define APPDATA_SUBDIR ".getdns" + struct passwd *p = getpwuid(getuid()); + char *home = NULL; + + if (!(home = p ? p->pw_dir : getenv("HOME"))) + DEBUG_ANCHOR("ERROR %s(): Could not get home directory\n" + , __FUNC__); + + else if ((len = strlen(home)) + sizeof(APPDATA_SUBDIR) + 2 >= PATH_MAX) + DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n" + , __FUNC__); + + else if (!strcpy(path, home)) + ; /* strcpy returns path always */ +#endif + else { + if (len == 0 || ( path[len - 1] != '/' + && path[len - 1] != '\\')) { + path[len++] = '/'; + path[len ] = '\0'; + } + (void) strcpy(path + len, APPDATA_SUBDIR); + len += sizeof(APPDATA_SUBDIR) - 1; + + if (mkdir(path, 0755) < 0 && errno != EEXIST) + DEBUG_ANCHOR("ERROR %s(): Could not mkdir %s: %s\n" + , __FUNC__, path, strerror(errno)); + else { + path[len++] = '/'; + path[len ] = '\0'; + return len; + } + } + return 0; +} + uint8_t *_getdns_context_get_priv_file(getdns_context *context, const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz) { char path[PATH_MAX]; - int n; - FILE *f; + FILE *f = NULL; + size_t len; - n = snprintf(path, sizeof(path), "%s/.getdns/%s", getenv("HOME"), fn); - if (n < 0 || n > PATH_MAX) - return NULL; + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); - if (!(f = fopen(path, "r"))) - return NULL; + else if (len + strlen(fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); - if ((*file_sz = fread(buf, 1, buf_len, f)) < (buf_len - 1) && feof(f)) + else if (!strcpy(path + len, fn)) + ; /* strcpy returns path + len always */ + + else if (!(f = fopen(path, "r"))) + DEBUG_ANCHOR("ERROR %s(): Opening \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); + + else if ((*file_sz = fread(buf, 1, buf_len, f)) < (buf_len - 1) && feof(f)) { buf[*file_sz] = 0; - + (void) fclose(f); + return buf; + } else if (fseek(f, 0, SEEK_END) < 0) - buf = NULL; + DEBUG_ANCHOR("ERROR %s(): Determining size of \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); - else if ((buf = GETDNS_XMALLOC( - context->mf, uint8_t, (buf_len = ftell(f) + 1)))) { + else if (!(buf = GETDNS_XMALLOC( + context->mf, uint8_t, (buf_len = ftell(f) + 1)))) + DEBUG_ANCHOR("ERROR %s(): Allocating %d memory for \"%s\"\n" + , __FUNC__, (int)buf_len, path); + else { rewind(f); if ((*file_sz = fread(buf, 1, buf_len, f)) >= buf_len || !feof(f)) { GETDNS_FREE(context->mf, buf); - buf = NULL; + DEBUG_ANCHOR("ERROR %s(): Reading \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); } - else + else { buf[*file_sz] = 0; + (void) fclose(f); + return buf; + } } - (void) fclose(f); - return buf; + if (f) + (void) fclose(f); + return NULL; } -void _getdns_context_write_priv_file(getdns_context *context, + +int _getdns_context_write_priv_file(getdns_context *context, const char *fn, getdns_bindata *content) { char path[PATH_MAX], tmpfn[PATH_MAX]; - int n, fd; - FILE *f; + int fd = -1; + FILE *f = NULL; + size_t len; - (void)context; + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); - DEBUG_ANCHOR("%s\n", __FUNC__); + else if (len + 6 >= sizeof(tmpfn) + || len + strlen(fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); - n = snprintf( path, sizeof( path), "%s/.getdns/%s" , getenv("HOME"), fn); - if (n < 0 || n > PATH_MAX) { - DEBUG_ANCHOR("Could not create filename for writing\n"); - return; - } - n = snprintf(tmpfn, sizeof(tmpfn), "%s/.getdns/XXXXXX", getenv("HOME")); - if (n < 0 || n > PATH_MAX) { - DEBUG_ANCHOR("Could not create tmpfn for writing\n"); - return; - } + else if (snprintf(tmpfn, sizeof(tmpfn), "%sXXXXXX", path) < 0) + DEBUG_ANCHOR("ERROR %s(): Creating temporary filename template\n" + , __FUNC__); - if ((fd = mkstemp(tmpfn)) < 0) { - DEBUG_ANCHOR("Could not create temporary file from \"%s\": %s\n", - tmpfn, strerror(errno)); - return; - } + else if (!strcpy(path + len, fn)) + ; /* strcpy returns path + len always */ - if (!(f = fdopen(fd, "w"))) { - close(fd); - return; - } - if (fwrite(content->data, 1, content->size, f) != content->size) - fclose(f); + else if ((fd = mkstemp(tmpfn)) < 0) + DEBUG_ANCHOR("ERROR %s(): Creating temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (!(f = fdopen(fd, "w"))) + DEBUG_ANCHOR("ERROR %s(): Opening temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (fwrite(content->data, 1, content->size, f) < content->size) + DEBUG_ANCHOR("ERROR %s(): Writing temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (fclose(f) < 0) + DEBUG_ANCHOR("ERROR %s(): Closing temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (rename(tmpfn, path) < 0) + DEBUG_ANCHOR("ERROR %s(): Renaming temporary file: %s\n" + , __FUNC__, strerror(errno)); else { - fclose(f); - if (rename(tmpfn, path) == -1) - DEBUG_ANCHOR("Could not mv \"%s\" \"%s\": %s\n", - tmpfn, path, strerror(errno)); + context->can_write_appdata = PROP_ABLE; + return 1; } + if (f) + (void) fclose(f); + + else if (fd >= 0) + (void) close(fd); + + context->can_write_appdata = PROP_UNABLE; + return 0; +} + +int _getdns_context_can_write_appdata(getdns_context *context) +{ + char test_fn[30], path[PATH_MAX]; + size_t len; + getdns_bindata test_content = { 4, (void *)"TEST" }; + + if (context->can_write_appdata == PROP_ABLE) + return 1; + + else if (context->can_write_appdata == PROP_UNABLE) + return 0; + + (void) snprintf( test_fn, sizeof(test_fn) + , "write-test-%d.tmp", arc4random()); + + if (!_getdns_context_write_priv_file(context, test_fn, &test_content)) + return 0; + + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); + + else if (len + strlen(test_fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); + + else if (!strcpy(path + len, test_fn)) + ; /* strcpy returns path + len always */ + + else if (unlink(path) < 0) + DEBUG_ANCHOR("ERROR %s(): Unlinking write test file \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); + + return 1; } getdns_return_t diff --git a/src/context.h b/src/context.h index 9bab5199..8acc8466 100644 --- a/src/context.h +++ b/src/context.h @@ -548,9 +548,11 @@ void _getdns_upstream_shutdown(getdns_upstream *upstream); uint8_t *_getdns_context_get_priv_file(getdns_context *context, const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz); -void _getdns_context_write_priv_file(getdns_context *context, +int _getdns_context_write_priv_file(getdns_context *context, const char *fn, getdns_bindata *content); +int _getdns_context_can_write_appdata(getdns_context *context); + void _getdns_upstream_reset(getdns_upstream *upstream); #endif /* _GETDNS_CONTEXT_H_ */