Skip to content

Commit 8ca95e4

Browse files
committed
Improving tile URI processing logic
1 parent dec0fc0 commit 8ca95e4

1 file changed

Lines changed: 106 additions & 13 deletions

File tree

src/mod_tile.c

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,9 +1088,45 @@ static int tile_storage_hook(request_rec *r)
10881088
return HTTP_NOT_FOUND;
10891089
}
10901090

1091+
/**
1092+
* Safely converts a string to an integer.
1093+
* Returns APR_SUCCESS if the whole string was a valid number.
1094+
*/
1095+
static apr_status_t safe_parse_int(const char *str, int *result)
1096+
{
1097+
char *endptr;
1098+
1099+
if (!str || *str == '\0') {
1100+
return APR_EINVAL;
1101+
}
1102+
1103+
// We use base 10 for coordinates
1104+
apr_int64_t val = apr_strtoi64(str, &endptr, 10);
1105+
1106+
// Check if we reached the end of the string.
1107+
// If *endptr isn't '\0', it means there was non-numeric garbage.
1108+
if (*endptr != '\0') {
1109+
return APR_EINVAL;
1110+
}
1111+
1112+
*result = (int)val;
1113+
return APR_SUCCESS;
1114+
}
1115+
1116+
// helper to split the extension from the filename (e.g., "12.png" -> "png")
1117+
static void get_extension(char *filename, char *ext_dest, size_t dest_len)
1118+
{
1119+
char *dot = strrchr(filename, '.');
1120+
1121+
if (dot) {
1122+
apr_cpystrn(ext_dest, dot + 1, dest_len);
1123+
*dot = '\0'; // Truncate the filename at the dot to leave just the number
1124+
}
1125+
}
1126+
10911127
static int tile_translate(request_rec *r)
10921128
{
1093-
int i, n, limit, oob;
1129+
int basuri_len, i, limit, oob;
10941130
char option[11];
10951131
char extension[256];
10961132

@@ -1124,18 +1160,19 @@ static int tile_translate(request_rec *r)
11241160

11251161
for (i = 0; i < scfg->configs->nelts; ++i) {
11261162
tile_config_rec *tile_config = &tile_configs[i];
1163+
basuri_len = strlen(tile_config->baseuri);
11271164

11281165
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s) extension(%s)",
11291166
tile_config->baseuri, tile_config->xmlname, tile_config->fileExtension);
11301167

1131-
if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
1168+
if (!strncmp(tile_config->baseuri, r->uri, basuri_len)) {
11321169

11331170
struct tile_request_data *rdata = (struct tile_request_data *)apr_pcalloc(r->pool, sizeof(struct tile_request_data));
11341171
struct protocol *cmd = (struct protocol *)apr_pcalloc(r->pool, sizeof(struct protocol));
11351172
bzero(cmd, sizeof(struct protocol));
11361173
bzero(rdata, sizeof(struct tile_request_data));
11371174

1138-
if (!strncmp(r->uri + strlen(tile_config->baseuri), "tile-layer.json", strlen("tile-layer.json"))) {
1175+
if (!strncmp(r->uri + basuri_len, "tile-layer.json", strlen("tile-layer.json"))) {
11391176
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Requesting tileJSON for tilelayer %s", tile_config->xmlname);
11401177
r->handler = "tile_json";
11411178
rdata->layerNumber = i;
@@ -1145,24 +1182,80 @@ static int tile_translate(request_rec *r)
11451182

11461183
char parameters[XMLCONFIG_MAX];
11471184

1148-
if (tile_config->enableOptions) {
1185+
// 1. Safety Check: Does the URI actually start with our baseuri?
1186+
if (strncmp(r->uri, tile_config->baseuri, basuri_len) != 0) {
1187+
return DECLINED;
1188+
}
1189+
1190+
// 2. Get the part of the URI after the base
1191+
char *path_info = apr_pstrdup(r->pool, r->uri + basuri_len);
1192+
1193+
// 3. Tokenize into an array
1194+
// We split by "/" to get parts like ["layername", "12", "123", "456.png", "option"]
1195+
apr_array_header_t *parts = apr_array_make(r->pool, 6, sizeof(char *));
1196+
char *last;
1197+
char *token = apr_strtok(path_info, "/", &last);
1198+
1199+
while (token) {
1200+
*(char **)apr_array_push(parts) = token;
1201+
token = apr_strtok(NULL, "/", &last);
1202+
}
1203+
1204+
char **elts = (char **)parts->elts;
1205+
int n_parts = parts->nelts;
1206+
1207+
// 4. Logic based on your options
1208+
if (tile_config->enableOptions && n_parts >= 4) {
1209+
// Expected: /parameter_options/z/x/y.ext/option
11491210
cmd->ver = PROTO_VER;
1150-
n = sscanf(r->uri + strlen(tile_config->baseuri), "%40[^/]/%d/%d/%d.%255[a-z]/%10s", parameters, &(cmd->z), &(cmd->x), &(cmd->y), extension, option);
1211+
apr_cpystrn(parameters, elts[0], 41);
11511212

1152-
if (n < 5) {
1153-
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s with options", tile_config->xmlname);
1213+
// Parse Z, X, Y safely
1214+
if (safe_parse_int(elts[1], &(cmd->z)) != APR_SUCCESS ||
1215+
safe_parse_int(elts[2], &(cmd->x)) != APR_SUCCESS) {
1216+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Z/X coordinates: %s/%s", elts[1], elts[2]);
11541217
return DECLINED;
11551218
}
1156-
} else {
1219+
1220+
// Special handling for elts[3] which is "y.ext"
1221+
char *y_str = apr_pstrdup(r->pool, elts[3]);
1222+
get_extension(y_str, extension, 256); // This null-terminates y_str at the dot
1223+
1224+
if (safe_parse_int(y_str, &(cmd->y)) != APR_SUCCESS) {
1225+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Y coordinate: %s", y_str);
1226+
return DECLINED;
1227+
}
1228+
1229+
if (n_parts >= 5) {
1230+
apr_cpystrn(option, elts[4], 11);
1231+
}
1232+
} else if (!tile_config->enableOptions && n_parts >= 3) {
1233+
// Expected: /z/x/y.ext/option
11571234
cmd->ver = 2;
1158-
n = sscanf(r->uri + strlen(tile_config->baseuri), "%d/%d/%d.%255[a-z]/%10s", &(cmd->z), &(cmd->x), &(cmd->y), extension, option);
1235+
parameters[0] = '\0';
11591236

1160-
if (n < 4) {
1161-
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s without options", tile_config->xmlname);
1237+
// Parse Z, X, Y safely
1238+
if (safe_parse_int(elts[0], &(cmd->z)) != APR_SUCCESS ||
1239+
safe_parse_int(elts[1], &(cmd->x)) != APR_SUCCESS) {
1240+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Z/X coordinates: %s/%s", elts[0], elts[1]);
11621241
return DECLINED;
11631242
}
11641243

1165-
parameters[0] = 0;
1244+
// Special handling for elts[2] which is "y.ext"
1245+
char *y_str = apr_pstrdup(r->pool, elts[2]);
1246+
get_extension(y_str, extension, 256); // This null-terminates y_str at the dot
1247+
1248+
if (safe_parse_int(y_str, &(cmd->y)) != APR_SUCCESS) {
1249+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Y coordinate: %s", y_str);
1250+
return DECLINED;
1251+
}
1252+
1253+
if (n_parts >= 4) {
1254+
apr_cpystrn(option, elts[3], 11);
1255+
}
1256+
} else {
1257+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid tile path structure");
1258+
return DECLINED;
11661259
}
11671260

11681261
if (strcmp(extension, tile_config->fileExtension) != 0) {
@@ -1211,7 +1304,7 @@ static int tile_translate(request_rec *r)
12111304

12121305
r->filename = NULL;
12131306

1214-
if ((tile_config->enableOptions && (n == 6)) || (!tile_config->enableOptions && (n == 5))) {
1307+
if ((tile_config->enableOptions && (n_parts == 5)) || (!tile_config->enableOptions && (n_parts == 4))) {
12151308
if (!strcmp(option, "status")) {
12161309
r->handler = "tile_status";
12171310
} else if (!strcmp(option, "dirty")) {

0 commit comments

Comments
 (0)