netcdf-c/include/nctestserver.h
Dennis Heimbigner 245961de00 re: github issues
https://github.com/Unidata/netcdf-c/issues/1168
    https://github.com/Unidata/netcdf-c/issues/1163
    https://github.com/Unidata/netcdf-c/issues/1162

This PR partially fixes memory leaks in the netcdf-c library,
in the ncdump utility, and in some test cases.

The netcdf-c library now runs memory clean with the assumption
that the --disable-utilities option is used. The primary remaining
problem is ncgen. Once that is fixed, I believe the netcdf-c library
will run memory clean with no limitations.

Notes
-----------
1. Memory checking was performed using gcc -fsanitize=address.
   Valgrind-based testing has yet to be performed.
2. The pnetcdf, hdf4, and examples code has not been tested.

Misc. Non-leak changes
1. Make tst_diskless2 only run when netcdf4 is enabled (issue 1162)
2. Fix CmakeLists.txt to turn off logging if ENABLE_NETCDF_4 is OFF
3. Isolated all my debug scripts into a single top-level directory
   called debug
4. Fix some USE_NETCDF4 dependencies in nc_test and nc_test4 Makefile.am
2018-10-30 20:48:12 -06:00

186 lines
4.6 KiB
C

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "netcdf.h"
#undef FINDTESTSERVER_DEBUG
#define MAXSERVERURL 4096
#define TIMEOUT 10 /*seconds*/
#define BUFSIZE 8192 /*bytes*/
#ifndef HAVE_CURLINFO_RESPONSE_CODE
#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
#endif
static int ping(const char* url);
static char**
parseServers(const char* remotetestservers)
{
char* rts;
char** servers = NULL;
char** list = NULL;
char* p;
char* svc;
char** l;
list = (char**)malloc(sizeof(char*) * (int)(strlen(remotetestservers)/2));
if(list == NULL) return NULL;
rts = strdup(remotetestservers);
if(rts == NULL) goto done;
l = list;
p = rts;
for(;;) {
svc = p;
p = strchr(svc,',');
if(p != NULL) *p = '\0';
*l++ = strdup(svc);
if(p == NULL) break;
p++;
}
*l = NULL;
servers = list;
list = NULL;
done:
if(rts) free(rts);
if(list) free(list);
return servers;
}
/**
Given a partial suffix path and a specified
protocol, test if a request to any of the test
servers + path returns some kind of result.
This indicates that the server is up and running.
Return the complete url for the server plus the path.
*/
static char*
nc_findtestserver(const char* path, int isdap4, const char* serverlist)
{
char** svclist;
char** svc;
char url[MAXSERVERURL];
char* match = NULL;
if((svclist = parseServers(serverlist))==NULL) {
fprintf(stderr,"cannot parse test server list: %s\n",serverlist);
return NULL;
}
for(svc=svclist;*svc;svc++) {
if(strlen(*svc) == 0)
goto done;
if(path == NULL) path = "";
if(strlen(path) > 0 && path[0] == '/')
path++;
/* Try https: first */
snprintf(url,MAXSERVERURL,"https://%s/%s",*svc,path);
if(ping(url) == NC_NOERR)
{match = strdup(url); goto done;}
/* Try http: next */
snprintf(url,MAXSERVERURL,"http://%s/%s",*svc,path);
if(ping(url) == NC_NOERR)
{match = strdup(url); goto done;}
}
done:
/* Free up the envv list of servers */
if(svclist != NULL) {
char** p;
for(p=svclist;*p;p++)
free(*p);
free(svclist);
}
return match;
}
#define CERR(expr) if((cstat=(expr)) != CURLE_OK) goto done;
struct Buffer {
char data[BUFSIZE];
size_t offset; /* into buffer */
};
static size_t
WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
struct Buffer* buffer = (struct Buffer*)data;
size_t total = size * nmemb;
size_t canwrite = total; /* assume so */
if(total == 0) {
fprintf(stderr,"WriteMemoryCallback: zero sized chunk\n");
goto done;
}
if((buffer->offset + total) > sizeof(buffer->data))
canwrite = (sizeof(buffer->data) - buffer->offset); /* partial read */
if(canwrite > 0)
memcpy(&(buffer->data[buffer->offset]),ptr,canwrite);
buffer->offset += canwrite;
done:
return total; /* pretend we captured everything */
}
/*
See if a server is responding.
Return NC_ECURL if the ping fails, NC_NOERR otherwise
*/
static int
ping(const char* url)
{
int stat = NC_NOERR;
CURLcode cstat = CURLE_OK;
CURL* curl = NULL;
long http_code = 0;
struct Buffer data;
/* Create a CURL instance */
curl = curl_easy_init();
if (curl == NULL) {cstat = CURLE_OUT_OF_MEMORY; goto done;}
CERR((curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1)));
/* Use redirects */
CERR((curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L)));
CERR((curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)));
/* use a very short timeout: 10 seconds */
CERR((curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)TIMEOUT)));
/* fail on HTTP 400 code errors */
CERR((curl_easy_setopt(curl, CURLOPT_FAILONERROR, (long)1)));
/* Set the URL */
CERR((curl_easy_setopt(curl, CURLOPT_URL, (void*)url)));
/* send all data to this function */
CERR((curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback)));
/* we pass our file to the callback function */
CERR((curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&data)));
data.offset = 0;
memset(data.data,0,sizeof(data.data));
CERR((curl_easy_perform(curl)));
/* Don't trust curl to return an error when request gets 404 */
CERR((curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &http_code)));
if(http_code >= 400) {
cstat = CURLE_HTTP_RETURNED_ERROR;
goto done;
}
done:
if(cstat != CURLE_OK) {
#ifdef FINDTESTSERVER_DEBUG
fprintf(stderr, "curl error: %s; url=%s\n",
curl_easy_strerror(cstat),url);
#endif
stat = NC_ECURL;
}
if (curl != NULL)
curl_easy_cleanup(curl);
return stat;
}