1 /* $OpenBSD: cddb.c,v 1.20 2015/01/16 06:40:06 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2002 Marc Espie. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 18 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/socket.h> 28 #include <netinet/in.h> 29 #include <sys/cdio.h> 30 #include <err.h> 31 #include <netdb.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <limits.h> 37 #include <vis.h> 38 #include "extern.h" 39 40 unsigned long cddb_sum(unsigned long); 41 void send_hello(FILE *); 42 void send_query(FILE *, int, struct cd_toc_entry *); 43 int further_query(FILE *, char *); 44 int connect_to(const char *, const char *); 45 int parse_connect_to(const char *, const char *); 46 char * get_line(FILE *); 47 char * get_answer(FILE *); 48 void verify_track_names(char **, int, struct cd_toc_entry *); 49 void safe_copy(char **, const char *); 50 51 unsigned long 52 cddb_sum(unsigned long v) 53 { 54 unsigned long sum = 0; 55 56 while (v > 0) { 57 sum += v % 10; 58 v /= 10; 59 } 60 return (sum); 61 } 62 63 unsigned long 64 cddb_discid(int n, struct cd_toc_entry *e) 65 { 66 unsigned long sum; 67 int i; 68 69 sum = 0; 70 for (i =0; i < n; i++) 71 sum += cddb_sum(entry2time(e+i)); 72 return (((sum % 0xff) << 24) | 73 ((entry2time(e+n) - entry2time(e)) << 8) | n); 74 } 75 76 void 77 send_hello(FILE *cout) 78 { 79 char hostname[HOST_NAME_MAX+1]; 80 81 if (gethostname(hostname, sizeof(hostname)) == -1) 82 strlcpy(hostname, "unknown", sizeof hostname); 83 fprintf(cout, "CDDB HELLO %s %s cdio " VERSION "\r\n", 84 getlogin(), hostname); 85 fflush(cout); 86 } 87 88 void 89 send_query(FILE *f, int n, struct cd_toc_entry *e) 90 { 91 int i; 92 93 fprintf(f, "cddb query %8lx %d", cddb_discid(n, e), n); 94 for (i = 0; i < n; i++) 95 fprintf(f, " %lu", entry2frames(e+i)); 96 fprintf(f, " %lu\r\n", (entry2frames(e+n)-entry2frames(e)) /75); 97 } 98 99 #define MAXSIZE 256 100 char copy_buffer[MAXSIZE]; 101 102 void 103 safe_copy(char **p, const char *title) 104 { 105 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 106 if (*p == NULL) 107 *p = strdup(copy_buffer); 108 else { 109 char *n; 110 111 if (asprintf(&n, "%s%s", *p, copy_buffer) == -1) 112 return; 113 free(*p); 114 *p = n; 115 } 116 } 117 118 int 119 further_query(FILE *cout, char *line) 120 { 121 char *key; 122 char *title; 123 124 key = strchr(line, ' '); 125 if (!key) 126 return 0; 127 *key++ = 0; 128 title = strchr(key, ' '); 129 if (!title) 130 return 0; 131 *title++ = 0; 132 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 133 printf("%s", copy_buffer); 134 strnvis(copy_buffer, line, MAXSIZE-1, VIS_TAB|VIS_NL); 135 printf("(%s)\n", copy_buffer); 136 fprintf(cout, "CDDB READ %s %s\r\n", line, key); 137 fflush(cout); 138 return 1; 139 } 140 141 142 int 143 connect_to(const char *host, const char *serv) 144 { 145 int s = -1; 146 struct addrinfo hints, *res0 = NULL, *res; 147 int error; 148 149 memset(&hints, 0, sizeof hints); 150 hints.ai_family = PF_UNSPEC; 151 hints.ai_socktype = SOCK_STREAM; 152 153 error = getaddrinfo(host, serv, &hints, &res0); 154 if (error) { 155 warnx("%s", gai_strerror(error)); 156 return -1; 157 } 158 159 for (res = res0; res; res = res->ai_next) { 160 s = socket(res->ai_family, res->ai_socktype, 161 res->ai_protocol); 162 if (s == -1) 163 continue; 164 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { 165 close(s); 166 s = -1; 167 continue; 168 } 169 break; 170 } 171 if (s == -1) 172 warn("cddb"); 173 freeaddrinfo(res0); 174 return s; 175 } 176 177 int 178 parse_connect_to(const char *host_port, const char *port) 179 { 180 int s; 181 char *last, *host; 182 183 host = (char *)host_port; 184 185 last = strrchr(host_port, ':'); 186 if (last != 0 && !(last != host && last[-1] == ':')) { 187 port = last + 1; 188 host = malloc(last - host_port + 1); 189 if (!host) 190 return -1; 191 memcpy(host, host_port, last-host_port); 192 host[last-host_port] = 0; 193 } 194 s = connect_to(host, port); 195 if (host != host_port) 196 free(host); 197 return s; 198 } 199 200 char * 201 get_line(FILE *cin) 202 { 203 char *line; 204 size_t len; 205 206 line = fgetln(cin, &len); 207 if (!line) 208 return NULL; 209 if (len == 0) 210 return NULL; 211 if (line[len-1] == '\n') 212 line[--len] = 0; 213 if (len != 0 && line[len-1] == '\r') 214 line[--len] = 0; 215 if (line[len] != 0) 216 return NULL; 217 return line; 218 } 219 220 char * 221 get_answer(FILE *cin) 222 { 223 char *line; 224 225 line = get_line(cin); 226 if (!line || *line != '2') 227 return NULL; 228 else 229 return line; 230 } 231 232 void 233 verify_track_names(char **names, int n, struct cd_toc_entry *e) 234 { 235 int i; 236 237 for (i = 0; i < n; i++) { 238 if (names[i] == 0) 239 names[i] = strdup(e->control & 4 ? "data" : "audio"); 240 } 241 } 242 243 char ** 244 cddb(const char *host_port, int n, struct cd_toc_entry *e, char *arg) 245 { 246 int s = -1; 247 int s2 = -1; 248 FILE *cin = NULL; 249 FILE *cout = NULL; 250 char *type; 251 char *line; 252 char **result = NULL; 253 int i; 254 const char *errstr; 255 256 s = parse_connect_to(host_port, "cddb"); 257 if (s == -1) 258 goto end; 259 s2 = dup(s); 260 if (s2 == -1) 261 goto end; 262 cin = fdopen(s, "r"); 263 if (!cin) { 264 warn("cddb: fdopen"); 265 goto end; 266 } 267 s = -1; 268 cout = fdopen(s2, "w"); 269 if (!cout) { 270 warn("cddb: fdopen"); 271 goto end; 272 } 273 s2 = -1; 274 line = get_answer(cin); 275 if (!line) { 276 warnx("cddb: won't talk to us"); 277 goto end; 278 } 279 280 send_hello(cout); 281 line = get_answer(cin); 282 if (!line) { 283 warnx("cddb: problem in hello"); 284 goto end; 285 } 286 287 send_query(cout, n, e); 288 fflush(cout); 289 line = get_answer(cin); 290 if (!line) { 291 warnx("cddb: problem in query"); 292 goto end; 293 } 294 type = strchr(line, ' '); 295 if (!type) 296 goto end; 297 *type++ = 0; 298 /* no match or other issue */ 299 if (strcmp(line, "202") == 0) { 300 printf("cddb: No match in database\n"); 301 goto end; 302 } 303 if (strcmp(line, "211") == 0 || strcmp(line, "212") == 0) { 304 int number = strtonum(arg, 0, INT_MAX, &errstr); 305 if (errstr != NULL && *arg != '\0') { 306 warnx("cddb: invalid index"); 307 goto end; 308 } 309 if (number == 0) { 310 if (strcmp(line, "211") == 0) 311 printf("cddb: multiple matches\n"); 312 else { 313 printf("cddb: inexact match\n"); 314 number = 1; 315 } 316 } 317 if (number == 0) { 318 for (i = 1;; i++) { 319 line = get_line(cin); 320 if (!line || strcmp(line, ".") == 0) 321 goto end; 322 printf("%d: %s\n", i, line); 323 } 324 } else { 325 int ok = 0; 326 327 for (i = 1;; i++) { 328 line = get_line(cin); 329 if (!line) 330 break; 331 if (strcmp(line, ".") == 0) 332 break; 333 if (i == number) 334 ok = further_query(cout, line); 335 } 336 if (!ok) 337 goto end; 338 } 339 } else if (strcmp(line, "200") != 0 || !further_query(cout, type)) 340 goto end; 341 result = calloc(sizeof(char *), n + 1); 342 if (!result) 343 goto end; 344 for (i = 0; i <= n; i++) 345 result[i] = NULL; 346 line = get_answer(cin); 347 if (!line) 348 goto end2; 349 for (;;) { 350 int k; 351 char *end; 352 353 line = get_line(cin); 354 if (!line) 355 goto end2; 356 if (strcmp(line, ".") == 0) 357 break; 358 if (strncmp(line, "TTITLE", 6) != 0) 359 continue; 360 line += 6; 361 end = strchr(line, '='); 362 if (end == NULL) 363 continue; 364 *end++ = '\0'; 365 k = strtonum(line, 0, n - 1, &errstr); 366 if (errstr != NULL) 367 continue; 368 safe_copy(&result[k], end); 369 } 370 fprintf(cout, "QUIT\r\n"); 371 verify_track_names(result, n, e); 372 goto end; 373 end2: 374 free(result); 375 result = NULL; 376 end: 377 if (cout) 378 fclose(cout); 379 if (cin) 380 fclose(cin); 381 if (s != -1) 382 close(s); 383 if (s2 != -1) 384 close(s2); 385 return result; 386 } 387 388 void 389 free_names(char **names) 390 { 391 int i; 392 393 for (i = 0; names[i]; i++) 394 free(names[i]); 395 free(names); 396 } 397