Skip to content

Commit f587654

Browse files
Make the dap4 code resistant to various server errors.
Some versions of some servers are returning malformed responses. Make the library either handle them or gracefully fail. The three server errors "fixed" here are as follows. 1. The attribute _NCProperties sometimes has a trailing nul character in its value. Soln is to elide the nul(s). 2. Sometimes a DAP response has no data part, only a DMR. Soln is to detect and return an error code instead of crashing. 3. Sometimes a server returns a redirection, but our current openmagic() function was not following the redirect. Soln is to follow redirects. Also because of #2, I am temporarily making --disable-dap-remote-tests be the default.
1 parent d9eb078 commit f587654

File tree

8 files changed

+77
-39
lines changed

8 files changed

+77
-39
lines changed

configure.ac

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,12 @@ fi
365365
# --enable-dap => enable-dap4
366366
enable_dap4=$enable_dap
367367
# Default is to do the short remote tests.
368+
# Temporary: Change default to npt do these tests
368369
AC_MSG_CHECKING([whether dap remote testing should be enabled (default on)])
369370
AC_ARG_ENABLE([dap-remote-tests],
370371
[AS_HELP_STRING([--disable-dap-remote-tests],
371372
[disable dap remote tests])])
372-
test "x$enable_dap_remote_tests" = xno || enable_dap_remote_tests=yes
373+
test "x$enable_dap_remote_tests" = xyes || enable_dap_remote_tests=no
373374
if test "x$enable_dap" = "xno" ; then
374375
enable_dap_remote_tests=no
375376
fi
@@ -399,7 +400,7 @@ AC_ARG_WITH([testservers],
399400
[REMOTETESTSERVERS=$with_testservers], [REMOTETESTSERVERS=no])
400401
msg="$REMOTETESTSERVERS"
401402
if test "x$REMOTETESTSERVERS" = xno ; then
402-
svclist="149.165.169.123:8080,remotetest.unidata.ucar.edu"
403+
svclist="remotetest.unidata.ucar.edu"
403404
REMOTETESTSERVERS="$svclist"
404405
fi
405406
AC_MSG_RESULT([$svclist])

libdap4/d4chunk.c

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ and whether it has checksums.
2828
*/
2929

3030
/* Define a local struct for convenience */
31-
struct HDR {unsigned int flags; unsigned int count;};
31+
struct HDR {unsigned int flags; unsigned int count;};
3232

3333
/* Forward */
3434
static void* getheader(void* p, struct HDR* hdr, int hostlittleendian);
@@ -60,15 +60,18 @@ NCD4_dechunk(NCD4meta* metadata)
6060
q = metadata->serial.rawdata;
6161
if(memcmp(q,"<?xml",strlen("<?xml"))==0
6262
|| memcmp(q,"<Dataset",strlen("<Dataset"))==0) {
63-
if(metadata->mode != NCD4_DMR)
64-
return THROW(NC_EDMR);
65-
/* setup as dmr only */
66-
metadata->serial.dmr = (char*)metadata->serial.rawdata; /* temp */
67-
metadata->serial.dmr[metadata->serial.rawsize-1] = '\0';
68-
metadata->serial.dmr = strdup((char *)q);
69-
if(metadata->serial.dmr == NULL)
70-
return THROW(NC_ENOMEM);
71-
return THROW(NC_NOERR);
63+
if(metadata->mode != NCD4_DMR)
64+
return THROW(NC_EDMR);
65+
/* setup as dmr only */
66+
metadata->serial.dmr = (char*)metadata->serial.rawdata; /* temp */
67+
/* Avoid strdup since rawdata might contain nul chars */
68+
if((metadata->serial.dmr = malloc(metadata->serial.rawsize+1)) == NULL)
69+
return THROW(NC_ENOMEM);
70+
memcpy(metadata->serial.dmr,metadata->serial.rawdata,metadata->serial.rawsize);
71+
metadata->serial.dmr[metadata->serial.rawsize-1] = '\0';
72+
/* Suppress nuls */
73+
(void)NCD4_elidenuls(metadata->serial.dmr,metadata->serial.rawsize);
74+
return THROW(NC_NOERR);
7275
}
7376

7477
/* We must be processing a DAP mode packet */
@@ -82,7 +85,7 @@ NCD4_dechunk(NCD4meta* metadata)
8285
/* Get the DMR chunk header*/
8386
p = getheader(p,&hdr,metadata->serial.hostlittleendian);
8487
if(hdr.count == 0)
85-
return THROW(NC_EDMR);
88+
return THROW(NC_EDMR);
8689
if(hdr.flags & ERR_CHUNK) {
8790
return processerrchunk(metadata, (void*)p, hdr.count);
8891
}
@@ -91,31 +94,40 @@ NCD4_dechunk(NCD4meta* metadata)
9194
metadata->localchecksumming = metadata->serial.remotechecksumming;
9295

9396
metadata->serial.remotelittleendian = ((hdr.flags & LITTLE_ENDIAN_CHUNK) ? 1 : 0);
94-
metadata->serial.dmr = (char*)p;
97+
/* Again, avoid strxxx operations on dmr */
98+
if((metadata->serial.dmr = malloc(hdr.count+1)) == NULL)
99+
return THROW(NC_ENOMEM);
100+
memcpy(metadata->serial.dmr,p,hdr.count);
95101
metadata->serial.dmr[hdr.count-1] = '\0';
96-
metadata->serial.dmr = strdup(metadata->serial.dmr);
97-
if(metadata->serial.dmr == NULL)
98-
return THROW(NC_ENOMEM);
99-
p += hdr.count;
102+
/* Suppress nuls */
103+
(void)NCD4_elidenuls(metadata->serial.dmr,hdr.count);
100104

101105
if(hdr.flags & LAST_CHUNK)
102106
return THROW(NC_ENODATA);
103107
/* Read and compress the data chunks */
104-
q = metadata->serial.dap;
108+
p = p + hdr.count; /* point to data chunk header */
109+
/* Do a sanity check in case the server has shorted us with no data */
110+
if((hdr.count + CHUNKHDRSIZE) >= metadata->serial.rawsize) {
111+
/* Server only sent the DMR part */
112+
metadata->serial.dapsize = 0;
113+
return THROW(NC_EDATADDS);
114+
}
115+
q = metadata->serial.dap;
105116
for(;;) {
106-
p = getheader(p,&hdr,metadata->serial.hostlittleendian);
107-
if(hdr.flags & ERR_CHUNK) {
117+
p = getheader(p,&hdr,metadata->serial.hostlittleendian);
118+
if(hdr.flags & ERR_CHUNK) {
108119
return processerrchunk(metadata, (void*)p, hdr.count);
109-
}
110-
/* data chunk; possibly last; possibly empty */
111-
if(hdr.count > 0) {
112-
d4memmove(q,p,hdr.count); /* will overwrite the header */
113-
p += hdr.count;
114-
q += hdr.count;
115-
}
116-
if(hdr.flags & LAST_CHUNK) break;
120+
}
121+
/* data chunk; possibly last; possibly empty */
122+
if(hdr.count > 0) {
123+
d4memmove(q,p,hdr.count); /* will overwrite the header */
124+
p += hdr.count;
125+
q += hdr.count;
126+
}
127+
if(hdr.flags & LAST_CHUNK) break;
117128
}
118129
metadata->serial.dapsize = (size_t)DELTA(q,metadata->serial.dap);
130+
119131
#ifdef D4DUMPDMR
120132
fprintf(stderr,"%s\n",metadata->serial.dmr);
121133
fflush(stderr);
@@ -169,8 +181,8 @@ getheader(void* p, struct HDR* hdr, int hostlittleendian)
169181
hyrax.count = *(unsigned int*)bytes; /* get count */
170182
/* See which makes more sense */
171183
if(hyrax.flags <= ALL_CHUNK_FLAGS && hyrax.count >= 0 && hyrax.count < hdr->count) {
172-
/* Use hyrax version */
173-
*hdr = hyrax;
184+
/* Use hyrax version */
185+
*hdr = hyrax;
174186
}
175187
#endif
176188
return p;
@@ -187,17 +199,17 @@ NCD4_infermode(NCD4meta* meta)
187199
char* raw = meta->serial.rawdata;
188200

189201
if(size < 16)
190-
return THROW(NC_EDAP); /* must have at least this to hold a hdr + partial dmr*/
202+
return THROW(NC_EDAP); /* must have at least this to hold a hdr + partial dmr*/
191203
if(memcmp(raw,"<?xml",strlen("<?xml"))==0
192204
|| memcmp(raw,"<Dataset",strlen("<Dataset"))==0) {
193-
meta->mode = NCD4_DMR;
194-
goto done;
205+
meta->mode = NCD4_DMR;
206+
goto done;
195207
}
196208
raw += 4; /* Pretend we have a DAP hdr */
197209
if(memcmp(raw,"<?xml",strlen("<?xml"))==0
198210
|| memcmp(raw,"<Dataset",strlen("<Dataset"))==0) {
199-
meta->mode = NCD4_DAP;
200-
goto done;
211+
meta->mode = NCD4_DAP;
212+
goto done;
201213
}
202214
/* Default to DSR */
203215
meta->mode = NCD4_DSR;

libdap4/d4util.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,22 @@ NCD4_entityescape(const char* s)
330330
return escaped;
331331
}
332332

333+
/* Elide all nul characters from an XML document as a precaution*/
334+
size_t
335+
NCD4_elidenuls(char* s, size_t slen)
336+
{
337+
size_t i,j;
338+
for(j=0,i=0;i<slen;i++) {
339+
int c = s[i];
340+
if(c != 0)
341+
s[j++] = (char)c;
342+
}
343+
/* if we remove any nuls then nul term */
344+
if(j < i)
345+
s[j] = '\0';
346+
return j;
347+
}
348+
333349
int
334350
NCD4_readfile(const char* filename, NCbytes* content)
335351
{

libdap4/ncd4.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ defined here, including function-like #defines.
4545
#define FIXEDOPAQUE
4646
#define DFALTOPAQUESIZE 16
4747

48+
/* Size of a chunk header */
49+
#define CHUNKHDRSIZE 4
50+
4851
/**************************************************/
4952

5053
#undef nullfree
@@ -137,6 +140,7 @@ extern char* NCD4_makeName(NCD4node*,const char* sep);
137140
extern int NCD4_parseFQN(const char* fqn0, NClist* pieces);
138141
extern char* NCD4_deescape(const char* esc);
139142
extern char* NCD4_entityescape(const char* s);
143+
extern size_t NCD4_elidenuls(char* s, size_t slen);
140144

141145
/* From d4dump.c */
142146
extern void NCD4_dumpbytes(size_t size, const void* data0, int swap);

libdispatch/derror.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ const char *nc_strerror(int ncerr1)
177177
case NC_EDAS:
178178
return "NetCDF: Malformed or inaccessible DAP DAS";
179179
case NC_EDDS:
180-
return "NetCDF: Malformed or inaccessible DAP DDS";
180+
return "NetCDF: Malformed or inaccessible DAP2 DDS or DAP4 DMR response";
181181
case NC_EDATADDS:
182-
return "NetCDF: Malformed or inaccessible DAP DATADDS";
182+
return "NetCDF: Malformed or inaccessible DAP2 DATADDS or DAP4 DAP response";
183183
case NC_EDAPURL:
184184
return "NetCDF: Malformed URL";
185185
case NC_EDAPCONSTRAINT:

libdispatch/dhttp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ setupconn(CURL* curl, const char* objecturl, NCbytes* buf)
263263
if (cstat != CURLE_OK) goto fail;
264264
cstat = CURLERR(curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1));
265265
if (cstat != CURLE_OK) goto fail;
266+
cstat = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
267+
if (cstat != CURLE_OK) goto fail;
266268

267269
if(buf != NULL) {
268270
/* send all data to this function */

libhdf5/nc4info.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ NC4_read_provenance(NC_FILE_INFO_T* file)
198198
provenance->superblockversion = superblock;
199199

200200
/* Read the _NCProperties value from the file */
201+
/* We do not return a size and assume the size is that upto the
202+
first nul character */
201203
if((ncstat = NC4_read_ncproperties(file,&propstring))) goto done;
202204
provenance->ncproperties = propstring;
203205
propstring = NULL;

nc_test/tst_byterange.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct TESTURLS {
3333
int format; /* instance of NC_FORMATX_XXX */
3434
const char* url;
3535
} testurls[] = {
36-
{NC_FORMAT_CLASSIC,"http://149.165.169.123:8080/thredds/fileServer/testdata/2004050300_eta_211.nc#bytes"},
36+
{NC_FORMAT_CLASSIC,"http://remotetest.unidata.ucar.edu/thredds/fileServer/testdata/2004050300_eta_211.nc#bytes"},
3737
#ifdef USE_NETCDF4
3838
{NC_FORMAT_NETCDF4,"http://noaa-goes16.s3.amazonaws.com/ABI-L1b-RadC/2017/059/03/OR_ABI-L1b-RadC-M3C13_G16_s20170590337505_e20170590340289_c20170590340316.nc#mode=bytes"},
3939
#endif
@@ -59,6 +59,7 @@ dotest(struct TESTURLS* test)
5959
int ncid;
6060
int format = -1;
6161

62+
fprintf(stderr,"Test: url=%s\n",test->url);
6263
/* First, try to open the url */
6364
if((ret = nc_open(test->url,0,&ncid))) return fail(ret);
6465

0 commit comments

Comments
 (0)