1 /* $NetBSD: uri.c,v 1.4 2026/07/09 16:54:11 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <string.h> 32 33 #include "ssl.h" 34 #include "ftp_var.h" 35 #include "uri.h" 36 37 typedef struct { 38 const char *buf; 39 size_t scheme; 40 size_t authority; 41 size_t path; 42 size_t query; 43 size_t fragment; 44 } uriparts_t; 45 46 #define SCHEME(p) ((p)->buf) 47 #define AUTHORITY(p) (SCHEME(p) + (p)->scheme) 48 #define PATH(p) (AUTHORITY(p) + (p)->authority) 49 #define QUERY(p) (PATH(p) + (p)->path) 50 #define FRAGMENT(p) (QUERY(p) + (p)->query) 51 52 static void 53 split_uri(const char *uri, uriparts_t *parts) 54 { 55 char *colon, *slash, *query, *sharp; 56 57 parts->buf = uri; 58 parts->scheme = 0; 59 parts->authority = 0; 60 parts->path = 0; 61 parts->query = 0; 62 parts->fragment = 0; 63 64 colon = strchr(uri, ':'); 65 slash = strchr(uri, '/'); 66 if (colon && (slash == NULL || (colon - uri) < (slash - uri))) { 67 parts->scheme = colon + 1 - uri; 68 uri = colon + 1; 69 } 70 71 if (uri[0] == '/' && uri[1] == '/') { 72 slash = strchr(uri + 2, '/'); 73 if (slash == NULL) { 74 parts->authority = strlen(uri); 75 return; 76 } 77 parts->authority = slash - uri; 78 uri = slash; 79 } 80 81 query = strchr(uri, '?'); 82 sharp = strchr(uri, '#'); 83 if (query && (sharp == NULL || (query - uri) < (sharp - uri))) { 84 parts->path = query - uri; 85 uri = query; 86 if (sharp != NULL) { 87 parts->query = sharp - query; 88 parts->fragment = strlen(sharp); 89 return; 90 } 91 parts->query = strlen(query); 92 return; 93 } 94 95 if (sharp != NULL) { 96 parts->path = sharp - uri; 97 parts->fragment = strlen(sharp); 98 return; 99 } 100 101 parts->path = strlen(uri); 102 } 103 104 static char * 105 build_uri(uriparts_t *s, uriparts_t *a, uriparts_t *p, 106 uriparts_t *q, uriparts_t *f) 107 { 108 size_t len; 109 char *buf, *out; 110 111 len = s->scheme + a->authority + p->path + q->query + f->fragment; 112 buf = ftp_malloc(len + 1); 113 114 out = buf; 115 memcpy(out, SCHEME(s), s->scheme); 116 out += s->scheme; 117 memcpy(out, AUTHORITY(a), a->authority); 118 out += a->authority; 119 memcpy(out, PATH(p), p->path); 120 out += p->path; 121 memcpy(out, QUERY(q), q->query); 122 out += q->query; 123 memcpy(out, FRAGMENT(f), f->fragment); 124 out += f->fragment; 125 *out = '\0'; 126 127 return buf; 128 } 129 130 static char * 131 remove_dot_segments(const char *path, size_t len) 132 { 133 char *p; 134 char *pbuf, *buf, *out, *seg; 135 size_t n; 136 137 p = pbuf = ftp_malloc(len + 1); 138 memcpy(p, path, len); 139 p[len] = '\0'; 140 141 buf = ftp_malloc(len + 1); 142 out = buf; 143 *out = '\0'; 144 145 while (len > 0) { 146 /* A */ 147 if (p[0] == '.') { 148 if (len > 1 && p[1] == '/') { 149 p += 2; 150 len -= 2; 151 continue; 152 } else if (len > 2 && p[1] == '.' && p[2] == '/') { 153 p += 3; 154 len -= 3; 155 continue; 156 } 157 } 158 159 /* B */ 160 if (p[0] == '/') { 161 if (len > 2 && p[1] == '.' && p[2] == '/') { 162 p += 2; 163 len -= 2; 164 continue; 165 } else if (len == 2 && p[1] == '.') { 166 p += 1; 167 len -= 1; 168 p[0] = '/'; 169 continue; 170 } 171 } 172 173 /* C */ 174 if (len > 2 && p[0] == '/' && p[1] == '.' && p[2] == '.') { 175 if (len > 3 && p[3] == '/') { 176 p += 3; 177 len -= 3; 178 seg = strrchr(buf, '/'); 179 if (seg != NULL) 180 *seg = '\0'; 181 continue; 182 } else if (len == 3) { 183 p += 2; 184 len -= 2; 185 p[0] = '/'; 186 seg = strrchr(buf, '/'); 187 if (seg != NULL) 188 *seg = '\0'; 189 continue; 190 } 191 } 192 193 /* D */ 194 if (p[0] == '.') { 195 if (len == 1) { 196 p += 1; 197 len -= 1; 198 continue; 199 } else if (len == 2 && p[1] == '.') { 200 p += 2; 201 len -= 2; 202 continue; 203 } 204 } 205 206 /* E */ 207 if (p[0] == '/') { 208 seg = strchr(p+1, '/'); 209 } else { 210 seg = strchr(p, '/'); 211 } 212 if (seg != NULL) { 213 n = seg - p; 214 if (n > len) 215 n = len; 216 } else 217 n = len; 218 219 memcpy(out, p, n); 220 out += n; 221 *out = '\0'; 222 p += n; 223 len -= n; 224 }; 225 226 FREEPTR(pbuf); 227 228 return buf; 229 } 230 231 static char * 232 merge_paths(uriparts_t *base, uriparts_t *ref) 233 { 234 char *buf; 235 const char *path, *last; 236 size_t len; 237 238 if (base->authority && base->path == 0) { 239 len = 1 + ref->path; 240 buf = ftp_malloc(len + 1); 241 buf[0] = '/'; 242 memcpy(buf + 1, PATH(ref), ref->path); 243 } else { 244 path = PATH(base); 245 last = strrchr(path, '/'); 246 if (last == NULL || (size_t)(last - path) > base->path) 247 len = 0; 248 else 249 len = (size_t)(last - path) + 1; 250 buf = ftp_malloc(len + ref->path + 1); 251 memcpy(buf, path, len); 252 memcpy(buf + len, PATH(ref), ref->path); 253 len += ref->path; 254 } 255 buf[len] = '\0'; 256 257 return buf; 258 } 259 260 char * 261 make_absurl(char *refurl, const char *baseurl) 262 { 263 uriparts_t base; 264 uriparts_t ref; 265 uriparts_t p; 266 char *buf, *tmp1, *tmp2; 267 268 split_uri(baseurl, &base); 269 split_uri(refurl, &ref); 270 271 p.scheme = p.authority = p.path = p.query = p.fragment = 0; 272 273 if (ref.scheme) { 274 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 275 p.buf = tmp1; 276 p.path = strlen(tmp1); 277 buf = build_uri(&ref, &ref, &p, &ref, &ref); 278 FREEPTR(tmp1); 279 } else { 280 if (ref.authority) { 281 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 282 p.buf = tmp1; 283 p.path = strlen(tmp1); 284 buf = build_uri(&base, &ref, &p, &ref, &ref); 285 FREEPTR(tmp1); 286 } else { 287 if (ref.path == 0) { 288 if (ref.query) { 289 buf = build_uri(&base, &base, &base, &ref, &ref); 290 } else { 291 buf = build_uri(&base, &base, &base, &base, &ref); 292 } 293 } else { 294 if (PATH(&ref)[0] == '/') { 295 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 296 p.buf = tmp1; 297 p.path = strlen(tmp1); 298 buf = build_uri(&base, &base, &p, &ref, &ref); 299 FREEPTR(tmp1); 300 } else { 301 tmp2 = merge_paths(&base, &ref); 302 tmp1 = remove_dot_segments(tmp2, strlen(tmp2)); 303 p.buf = tmp1; 304 p.path = strlen(tmp1); 305 FREEPTR(tmp2); 306 307 buf = build_uri(&base, &base, &p, &ref, &ref); 308 FREEPTR(tmp1); 309 } 310 } 311 } 312 } 313 314 FREEPTR(refurl); 315 316 return buf; 317 } 318