1 /* $NetBSD: uri.c,v 1.5 2026/07/18 04:32:38 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 = memchr(p+1, '/', len-1); 209 } else { 210 seg = memchr(p, '/', len); 211 } 212 if (seg != NULL) 213 n = seg - p; 214 else 215 n = len; 216 217 memcpy(out, p, n); 218 out += n; 219 *out = '\0'; 220 p += n; 221 len -= n; 222 }; 223 224 FREEPTR(pbuf); 225 226 return buf; 227 } 228 229 static char * 230 merge_paths(uriparts_t *base, uriparts_t *ref) 231 { 232 char *buf; 233 const char *path, *last; 234 size_t len; 235 236 if (base->authority && base->path == 0) { 237 len = 1 + ref->path; 238 buf = ftp_malloc(len + 1); 239 buf[0] = '/'; 240 memcpy(buf + 1, PATH(ref), ref->path); 241 } else { 242 path = PATH(base); 243 last = strrchr(path, '/'); 244 if (last == NULL || (size_t)(last - path) > base->path) 245 len = 0; 246 else 247 len = (size_t)(last - path) + 1; 248 buf = ftp_malloc(len + ref->path + 1); 249 memcpy(buf, path, len); 250 memcpy(buf + len, PATH(ref), ref->path); 251 len += ref->path; 252 } 253 buf[len] = '\0'; 254 255 return buf; 256 } 257 258 char * 259 make_absurl(char *refurl, const char *baseurl) 260 { 261 uriparts_t base; 262 uriparts_t ref; 263 uriparts_t p; 264 char *buf, *tmp1, *tmp2; 265 266 split_uri(baseurl, &base); 267 split_uri(refurl, &ref); 268 269 p.scheme = p.authority = p.path = p.query = p.fragment = 0; 270 271 if (ref.scheme) { 272 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 273 p.buf = tmp1; 274 p.path = strlen(tmp1); 275 buf = build_uri(&ref, &ref, &p, &ref, &ref); 276 FREEPTR(tmp1); 277 } else { 278 if (ref.authority) { 279 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 280 p.buf = tmp1; 281 p.path = strlen(tmp1); 282 buf = build_uri(&base, &ref, &p, &ref, &ref); 283 FREEPTR(tmp1); 284 } else { 285 if (ref.path == 0) { 286 if (ref.query) { 287 buf = build_uri(&base, &base, &base, &ref, &ref); 288 } else { 289 buf = build_uri(&base, &base, &base, &base, &ref); 290 } 291 } else { 292 if (PATH(&ref)[0] == '/') { 293 tmp1 = remove_dot_segments(PATH(&ref), ref.path); 294 p.buf = tmp1; 295 p.path = strlen(tmp1); 296 buf = build_uri(&base, &base, &p, &ref, &ref); 297 FREEPTR(tmp1); 298 } else { 299 tmp2 = merge_paths(&base, &ref); 300 tmp1 = remove_dot_segments(tmp2, strlen(tmp2)); 301 p.buf = tmp1; 302 p.path = strlen(tmp1); 303 FREEPTR(tmp2); 304 305 buf = build_uri(&base, &base, &p, &ref, &ref); 306 FREEPTR(tmp1); 307 } 308 } 309 } 310 } 311 312 FREEPTR(refurl); 313 314 return buf; 315 } 316