/* * TCP/IP IO communication implementation for * bjnp backend for the Common UNIX Printing System (CUPS). * Copyright 2008 by Louis Lagendijk * * These coded instructions, statements, and computer programs are the * property of Louis Lagendijk and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "LICENSE.txt" * "LICENSE" which should have been included with this file. If this * file is missing or damaged, see the license at "http://www.cups.org/". * * This file is subject to the Apple OS-Developed Software exception. * * Contents: * Utility functions */ #include #include #include #include #include "bjnp.h" int sa_is_equal( const http_addr_t * sa1, const http_addr_t * sa2) { if ((sa1 == NULL) || (sa2 == NULL) ) return 0; if (sa1->addr.sa_family == sa2-> addr.sa_family) { if( sa1 -> addr.sa_family == AF_INET) { if ( (sa1->ipv4.sin_port == sa2->ipv4.sin_port) && (sa1->ipv4.sin_addr.s_addr == sa2->ipv4.sin_addr.s_addr)) { return 1; } } #ifdef ENABLE_IPV6 else if (sa1 -> addr.sa_family == AF_INET6 ) { if ( (sa1-> ipv6.sin6_port == sa2->ipv6.sin6_port) && (memcmp(&(sa1->ipv6.sin6_addr), &(sa2->ipv6.sin6_addr), sizeof(struct in6_addr)) == 0)) { return 1; } } #endif } return 0; } int sa_size( const http_addr_t *sa) { switch (sa -> addr.sa_family) { case AF_INET: return (sizeof(struct sockaddr_in) ); #ifdef ENABLE_IPV6 case AF_INET6: return (sizeof(struct sockaddr_in6) ); #endif default: /* should not occur */ return sizeof( http_addr_t ); } } int get_protocol_family( const http_addr_t *sa) { switch (sa -> addr.sa_family) { case AF_INET: return PF_INET; break; #ifdef ENABLE_IPV6 case AF_INET6: return PF_INET6; break; #endif default: /* should not occur */ return -1; } } void get_address_info ( const http_addr_t *addr, char * addr_string, int *port) { char tmp_addr[BJNP_HOST_MAX]; if ( addr->addr.sa_family == AF_INET) { inet_ntop( AF_INET, &(addr -> ipv4.sin_addr.s_addr), addr_string, BJNP_HOST_MAX); *port = ntohs (addr->ipv4.sin_port); } #ifdef ENABLE_IPV6 else if (addr->addr.sa_family == AF_INET6) { inet_ntop( AF_INET6, addr -> ipv6.sin6_addr.s6_addr, tmp_addr, sizeof(tmp_addr) ); if (IN6_IS_ADDR_LINKLOCAL( &(addr -> ipv6.sin6_addr) ) ) sprintf(addr_string, "[%s%%%d]", tmp_addr, addr -> ipv6.sin6_scope_id); else sprintf(addr_string, "[%s]", tmp_addr); *port = ntohs (addr->ipv6.sin6_port); } #endif else { /* unknown address family, should not occur */ strcpy(addr_string, "Unknown address family"); *port = 0; } } int parse_IEEE1284_to_model (char *printer_id, char *model) { /* * parses the IEEE1284 ID of the printer to retrieve make and model * of the printer * Returns: 0 = not found * 1 = found, model is set */ char s[BJNP_IEEE1284_MAX]; char *tok; strcpy (s, printer_id); model[0] = '\0'; tok = strtok (s, ";"); while (tok != NULL) { /* DES contains make and model */ if (strncmp (tok, "DES:", 4) == 0) { strcpy (model, tok + 4); return 1; } tok = strtok (NULL, ";"); } return 0; } int charTo2byte (char d[], char s[], int len) { /* * copy ASCII string to 2 byte unicode string * Returns: number of characters copied */ int done = 0; int copied = 0; int i; for (i = 0; i < len; i++) { d[2 * i] = '\0'; if (s[i] == '\0') { done = 1; } if (done == 0) { d[2 * i + 1] = s[i]; copied++; } else d[2 * i + 1] = '\0'; } return copied; } void u8tohex_string( uint8_t * input, char * str, int size) { static const char hdigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int i; for (i = 0; i < size; i++) { str[ 2 * i] = hdigit[ (input[i] >> 4) & 0xf]; str[ 2 * 1 +1] = hdigit[ input[i] & 0xf]; } str[2 * i] = '\0'; } int find_bin_string (const void *in, int len, char *lookfor, int size) { /* * looks for a new print command in the input stream * Returns: offset where lookfor is found or -1 when not found */ int i; const char *buf = in; /* start at offset 1 to avoid match at start of input */ for (i = 1; i < (len - size); i++) { if ((buf[i] == lookfor[0]) && (memcmp (buf + i, lookfor, size) == 0)) { return i; } } return -1; } bjnp_address_type_t get_printer_host (const http_addr_t *printer_addr, char *name, int *port) { /* * lookup hostname for printers address */ struct addrinfo *results; struct addrinfo *result; http_addr_t *res_address; char ip_address[BJNP_HOST_MAX]; char service[64]; int error; int match = 0; bjnp_address_type_t level; #ifdef ENABLE_IPV6 if ( ( printer_addr -> addr.sa_family == AF_INET6 ) && ( IN6_IS_ADDR_LINKLOCAL( &(printer_addr -> ipv6.sin6_addr ) ) ) ) level = BJNP_ADDRESS_IS_LINK_LOCAL; else level = BJNP_ADDRESS_IS_GLOBAL; #else level = BJNP_ADDRESS_IS_GLOBAL; #endif get_address_info( printer_addr, ip_address, port); bjnp_debug (LOG_INFO, "Found printer at ip address: %s\n", ip_address); /* do reverse name lookup, if hostname can not be found return IP-address */ if( (error = getnameinfo( &(printer_addr-> addr) , sa_size( printer_addr), name, BJNP_HOST_MAX , NULL, 0, NI_NAMEREQD) ) != 0 ) { bjnp_debug(LOG_DEBUG, "Name for %s not found : %s\n", ip_address, gai_strerror(error) ); strcpy( name, ip_address ); return level; } /* some buggy routers return rubbish if reverse lookup fails, so * we do a forward lookup to see if the result matches the ip-address */ sprintf( service, "%d", *port); if (getaddrinfo( name, service, NULL, &results) == 0) { result = results; while (result != NULL) { res_address = (http_addr_t *)result-> ai_addr; if(sa_is_equal( res_address, printer_addr)) { /* found match, good */ match = 1; break; } result = result-> ai_next; } freeaddrinfo(results); if (match == 1) { bjnp_debug (LOG_DEBUG, "Reverse lookup for %s succeeded, using as hostname\n", name); level = BJNP_ADDRESS_HAS_FQDN; } else { bjnp_debug (LOG_DEBUG, "Reverse lookup for %s succeeded, forward lookup failed, using IP-address %s instead\n", name, ip_address); strcpy (name, ip_address); } } else { /* lookup failed, use ip-address */ bjnp_debug (LOG_DEBUG, "Reverse lookup of %s failed, using IP-address %s\n", name, ip_address); strcpy (name, ip_address); } return level; } char * bjnp_map_status(cups_sc_status_t status) { switch(status) { case CUPS_SC_STATUS_BAD_MESSAGE: return "bad message"; case CUPS_SC_STATUS_IO_ERROR: return "I/O error"; case CUPS_SC_STATUS_NONE: return "no status"; case CUPS_SC_STATUS_NOT_IMPLEMENTED: return "not implemented"; case CUPS_SC_STATUS_NO_RESPONSE: return "no response"; case CUPS_SC_STATUS_OK: return "status ok"; case CUPS_SC_STATUS_TIMEOUT: return "timeout"; case CUPS_SC_STATUS_TOO_BIG: return "message too big"; default: return "unknown status, please report!"; } }