diff options
author | Didier Raboud <odyx@debian.org> | 2020-10-03 17:47:15 +0200 |
---|---|---|
committer | Didier Raboud <odyx@debian.org> | 2020-10-03 17:47:15 +0200 |
commit | c3dcb444f2b2ef562a316b6324bb49599ca25841 (patch) | |
tree | cb95b1749ab90a8e239e9541d5ee5163a0c52157 /protocol | |
parent | d7163f9ff2653bf7b37a8e535edeccf2c42be9fe (diff) |
New upstream version 3.20.9
Diffstat (limited to 'protocol')
-rw-r--r-- | protocol/discovery/avahiDiscovery.c | 521 | ||||
-rw-r--r-- | protocol/discovery/avahiDiscovery.h | 88 | ||||
-rw-r--r-- | protocol/discovery/mdns.c | 496 | ||||
-rw-r--r-- | protocol/discovery/mdns.h | 104 | ||||
-rw-r--r-- | protocol/hp_ipp.c | 11 | ||||
-rw-r--r-- | protocol/hp_ipp.h | 4 |
6 files changed, 617 insertions, 607 deletions
diff --git a/protocol/discovery/avahiDiscovery.c b/protocol/discovery/avahiDiscovery.c new file mode 100644 index 000000000..8d325ffc0 --- /dev/null +++ b/protocol/discovery/avahiDiscovery.c @@ -0,0 +1,521 @@ +/* + * This file is derived from avahi_client_browse.c, part of avahi. + * + * avahi is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * avahi is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with avahi; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +#include "avahiDiscovery.h" +#include <errno.h> +#include <dbus/dbus.h> + +DBusConnection *conn; + +char* aUriBuf=NULL; +char ipAddressBuff[MAX_IP_ADDR_LEN]={'\0'}; +static int aBytesRead = 0; +static AvahiSimplePoll *aSimplePoll = NULL; +static int aMemAllocated = 0; + +/* +This function will fill the dictionary arguments for the dbus function call +*/ +static void addDictWithStringValue(DBusMessageIter *iter, + const char *key, const char *str) +{ + DBusMessageIter dict, entry, value; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, &value); + dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); + dbus_message_iter_close_container(&entry, &value); + + dbus_message_iter_close_container(&dict, &entry); + dbus_message_iter_close_container(iter, &dict); +} +/* +This function will form the empty dictionary for the dbus function call +*/ +static void addEmptyStringDict(DBusMessageIter *iter) +{ + DBusMessageIter dict; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dbus_message_iter_close_container(iter, &dict); +} +/* +This function will fill the arguments for the dbus function call +*/ +static void addArgumentsForAuthentication(DBusConnection *conn, DBusMessageIter *iter) +{ + const char *busname = dbus_bus_get_unique_name(conn); + const char *kind = SYSTEM_BUS_NAME; + const char *action = ACTION_ID; + + const char *cancel = ""; + dbus_uint32_t flags = 1; + DBusMessageIter subject; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &subject); + dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); + addDictWithStringValue(&subject, "name", busname); + dbus_message_iter_close_container(iter, &subject); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); + addEmptyStringDict(iter); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); +} +/* +This function will start the avahi service if the process authorized by polkit +*/ +static bool systemdStartAvahiService() +{ + DBusMessage *msg, *response; + DBusError err; + + dbus_error_init(&err); + + const char *aAvahiUnitName = AVAHI_SERVICE_NAME; + const char *aAvahiUnitMode = AVHHI_SERVICE_MODE_REPLACE; + + msg = dbus_message_new_method_call(SYSTEMD_DBUS_NAME, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_INTF, SYSTEMD_START_SERVICE_METHOD); + if(!msg) + { + BUG("failed to create dbus message\n"); + return false; + } + dbus_message_append_args(msg, DBUS_TYPE_STRING, &aAvahiUnitName, DBUS_TYPE_STRING, &aAvahiUnitMode, DBUS_TYPE_INVALID); + + response = dbus_connection_send_with_reply_and_block(conn, msg,SYSTEMD_SERVICE_TIMEOUT, &err); + + if(dbus_error_is_set(&err)) + { + dbus_message_unref(msg); + BUG("failed to start service : %s\n", err.message); + return false; + } + + dbus_message_unref(msg); + dbus_message_unref(response); + return true; +} +/* +This function will involk polkit based authorization using dbus +*/ +static bool checkAuthorizationForAvahiService() +{ + DBusMessage *msg, *reply; + DBusMessageIter iter; + DBusError err; + bool authorized = false; + conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (!conn) + { + BUG("Can't get on system bus"); + return authorized; + } + msg = dbus_message_new_method_call(POLKIT_AUTH_DBUS, POLKIT_AUTH_PATH, + POLKIT_AUTH_INTF, POLKIT_AUTH_METHOD_CALL); + if (!msg) + { + BUG("Can't allocate new method call\n"); + return authorized; + } + dbus_message_iter_init_append(msg, &iter); + addArgumentsForAuthentication(conn, &iter); + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_TIMEOUT_INFINITE, &err); + dbus_message_unref(msg); + if (!reply) + { + if (dbus_error_is_set(&err)) + { + BUG("%s\n", err.message); + dbus_error_free(&err); + } + else + { + BUG("Can't check authorization\n"); + } + return authorized; + } + //verify the response with dbus response signature + if (dbus_message_has_signature(reply, "(bba{ss})")) + { + DBusMessageIter result; + dbus_bool_t respAuthorized; + //iterate the dbus response + dbus_message_iter_init(reply, &iter); + dbus_message_iter_recurse(&iter, &result); + //get the authorization status + dbus_message_iter_get_basic(&result, &respAuthorized); + DBG("Authorized %d \n", respAuthorized); + if(respAuthorized) + { + authorized = true; + } + } + else + { + BUG("dbus response signature mismatch\n"); + } + dbus_message_unref(reply); + return authorized; +} + +/* +This function will get the value for the given key if found in the string list. +Returns true if found the value ,otherwise returns false +Parameters: + iStrList - INPUT - Avahi device (scanner) string list. + ikey - INPUT - Key to find in the stringlist. + oKeyValue - OUTPUT - Value of the given key. + aMdlStrLen - OUTPUT - size of oKeyValue. +Note: + Possible value in iStrList (avahi response for a device) + "duplex=F" "is=platen" "cs=binary,color,grayscale" "rs=eSCL" "representation=images/printer.png" + "UUID=6fcbd42d-b117-5352-ab4c-35acfabefc34" "note=" "adminurl=http://HPE4E749F6A0F7.local." + "ty=HP DeskJet 2700 series" "pdl=application/octet-stream,application/pdf,image/jpeg" + "mopria-certified-scan=1.2" "vers=2.63" "txtvers=1" +*/ + +static bool getHPScannerModel(AvahiStringList *iStrList, const char *ikey,char **oKeyValue,size_t *aMdlStrLen) +{ + AvahiStringList *aStrList = NULL; + bool aValueFound = false; + char *aKey = NULL; + aStrList = avahi_string_list_find(iStrList, ikey); + /* + aStrList will be Null ,if given key is not found, + avahi_string_list_get_pair will return zero if key found, + also need to process the response form HP scanner only. + */ + if( ( aStrList != NULL ) && ( avahi_string_list_get_pair(aStrList, &aKey, oKeyValue, aMdlStrLen) == 0 ) + && ( *oKeyValue != NULL ) && ( strncmp(MFG_HP,*oKeyValue,MFG_HP_LEN) == 0 )) + { + size_t aIndex = 0; + for(aIndex = 0; aIndex<(*aMdlStrLen); aIndex++) + { + if(isspace((*oKeyValue)[aIndex])) + { + (*oKeyValue)[aIndex] = '_'; + } + (*oKeyValue)[aIndex] = tolower((*oKeyValue)[aIndex]); + } + DBG("oKeyValue is %s\n", *oKeyValue); + aValueFound = true; + } + if( aKey != NULL ) + avahi_free( (void *)aKey ); + return aValueFound; +} + +/* +This function will gets called whenever a service has been resolved successfully or timed out +*/ +static void resolve_callback( + AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + assert(r); + switch (event) { + case AVAHI_RESOLVER_FAILURE: + BUG( "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + + case AVAHI_RESOLVER_FOUND: { + char aIPAddress[AVAHI_ADDRESS_STR_MAX]={'\0'}; + char *aMdlStr = NULL; + size_t aMdlStrLen = 0 ; + DBG( "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); + avahi_address_snprint(aIPAddress, AVAHI_ADDRESS_STR_MAX, address); + DBG("avahi_address_snprint : \n %s \n",aIPAddress); + if( getHPScannerModel(txt,TYPE_NAME,&aMdlStr,&aMdlStrLen) == true ) + { + DBG("aMdlStr name is %s \n",aMdlStr); + DBG("aMdlStrLen is %zu \n",aMdlStrLen); + char aTempUri[MAX_URI_LEN] = {'\0'}; + snprintf( aTempUri, MAX_URI_LEN, "hp:/net/%s?ip=%s&queue=false", aMdlStr+HP_SKIP_MFG_NAME_SIZE, aIPAddress); + if(aUriBuf == NULL) + { + aUriBuf = (char *)calloc(HP_MAX_SCAN_BUFF,sizeof(char)); + if(aUriBuf == NULL) + { + BUG("Unable to alloacate the memeory\n"); + exit(0); + } + aMemAllocated = HP_MAX_SCAN_BUFF ; + } + //Check whether buffer has enough space to add new URI and check for duplicate URIs. + if( !strstr(aUriBuf, aTempUri) ) + { + if ( (aBytesRead + MAX_URI_LEN) > aMemAllocated ) + { + aUriBuf = realloc(aUriBuf, ( MAX_URI_LEN * sizeof(char) ) ); + aMemAllocated = aMemAllocated + HP_EXT_SCAN_BUFF; + } + aBytesRead += snprintf(aUriBuf + aBytesRead, MAX_URI_LEN,"%s;", aTempUri); + } + } + if( aMdlStr != NULL ) + avahi_free( (void *)aMdlStr ); + break; + } + } + //avahi_service_resolver_free(r); +} +/* Called whenever a new services becomes available on the LAN or is removed from the LAN */ +static void browse_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) { + + AvahiClient *c = (AvahiClient *)userdata; + assert(b); + switch (event) { + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_REMOVE: + break; + + case AVAHI_BROWSER_FAILURE: + + BUG( "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + avahi_simple_poll_quit(aSimplePoll); + return; + + case AVAHI_BROWSER_NEW: + BUG( "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + + /* We ignore the returned resolver object. In the callback + function we free it. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + + if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_INET, (AvahiLookupFlags)0, resolve_callback, c))) + BUG( "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + avahi_simple_poll_quit(aSimplePoll); + break; + } +} +/* Called whenever the client or server state changes */ +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + if (state == AVAHI_CLIENT_FAILURE) { + BUG( "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_simple_poll_quit(aSimplePoll); + } +} +/* +This function will gets called whenever a host name has been resolved successfully or timed out +*/ +static void host_name_resolver_callback( + AvahiHostNameResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const AvahiAddress *a, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void *userdata) { + + assert(r); + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + avahi_address_snprint(ipAddressBuff, sizeof(ipAddressBuff), a); + DBG("%s\t%s\n", name, ipAddressBuff); + avahi_simple_poll_quit(aSimplePoll); + break; + } + case AVAHI_RESOLVER_FAILURE: + BUG( "Failed to resolve host name '%s'\n", name); + avahi_simple_poll_quit(aSimplePoll); + break; + } + avahi_host_name_resolver_free(r); +} +/* +This function will register the callbacks for the avahi calls. +Parameters: + iCommandType - INPUT - Lookup / Probe scanner. + iHostName - INPUT - Host name of the scanner. +Note: + iHostName will be empty for command type AVAHI_NET_DISCOVERY +*/ +static void avahi_setup(const int iCommandType, const char* iHostName) +{ + + AvahiClient *client = NULL; + AvahiServiceBrowser *sb = NULL; + int error = 0; + if (!(aSimplePoll = avahi_simple_poll_new())) + { + BUG( "Failed to create simple poll object.\n"); + goto fail; + } + + if (!(client = avahi_client_new(avahi_simple_poll_get(aSimplePoll), AVAHI_CLIENT_IGNORE_USER_CONFIG, client_callback, NULL, &error))) + { + //if Daemon connection failed or daemon not running, + //call systemctl for authorization once it authorized start the service + if ( ( ( error == AVAHI_ERR_NO_DAEMON ) || (error == AVAHI_ERR_DISCONNECTED) ) + && checkAuthorizationForAvahiService() && systemdStartAvahiService() ) + { + if (!(client = avahi_client_new(avahi_simple_poll_get(aSimplePoll), AVAHI_CLIENT_IGNORE_USER_CONFIG, client_callback, NULL, &error))) + { + BUG( "Failed to create client object: %s\n", avahi_strerror(error)); + goto fail; + } + } + else + { + BUG( "Failed to create client object: %s\n", avahi_strerror(error)); + goto fail; + } + } + + if ( iCommandType == AVAHI_NET_DISCOVERY ) /* Probe network scanner */ + { + /* Create the service browser */ + if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_uscan._tcp", NULL, (AvahiLookupFlags)0, browse_callback, client))) + { + //if Daemon connection failed or daemon not running, + //call systemctl for authorization once it authorized start the service + if ( ( ( error == AVAHI_ERR_NO_DAEMON ) || (error == AVAHI_ERR_DISCONNECTED) ) + && checkAuthorizationForAvahiService() && systemdStartAvahiService() ) + { + if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_uscan._tcp", NULL, (AvahiLookupFlags)0, browse_callback, client))) + { + BUG( "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + } + else + { + BUG( "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + } + + /* Create the service browser */ + if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_scanner._tcp", NULL, (AvahiLookupFlags)0, browse_callback, client))) + { + BUG( "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + } + else if ( iCommandType == AVAHI_HOST_LOOKUP ) /* Find the IP (IPV4) address of given host name */ + { + if ( !(avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, iHostName, AVAHI_PROTO_INET, (AvahiLookupFlags)0, + host_name_resolver_callback, NULL) ) ) + { + BUG( "Failed to create host name resolver: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + } + avahi_simple_poll_loop(aSimplePoll); +fail: + if (client) + avahi_client_free(client); + if (aSimplePoll) + avahi_simple_poll_free(aSimplePoll); +} +/* +This function will return URIs of the scanner found in +the network. +Parameters: + oUrisBuf - OUTPUT - Buffer to hold URIs of Scanner + iBufsize - INPUT - Size of output buffer. +Note: + URIs will be splited useing ';' delimiter +*/ +int avahi_probe_nw_scanners() +{ + int aRet = AVAHI_STATUS_ERROR; + avahi_setup(AVAHI_NET_DISCOVERY,""); + //size_t aStrLen = strlen(aUriBuf); + if(aBytesRead > 0) + { + aRet = AVAHI_STATUS_OK; + } + return aRet; +} +/* +This function will return the IP address of given hostname +Parameters: + iHostName - INPUT - Hostname of the scanner + oIP - OUTPUT - IP Address of given host name +Note: + Output IP address will be in IPV4 format +*/ +int avahi_lookup(const char *iHostName) +{ + int aRet = AVAHI_STATUS_ERROR; + char aUpdatedHostname[MAX_NAME_LENGTH] = {0}; + int aHostNameLen = strlen(iHostName); + size_t aStrLen = 0; + //DNS host name size is 256 inclusive of '.local' + //So the incoming host name length should be less than or equal to 250 + if ( aHostNameLen <= ( MAX_NAME_LENGTH - APPEND_LOCAL_LEN ) ) + { + //Host name should be appended with .local + snprintf(aUpdatedHostname,MAX_NAME_LENGTH,"%s.local", iHostName); + avahi_setup(AVAHI_HOST_LOOKUP,aUpdatedHostname); + aStrLen = strlen(ipAddressBuff); + if ( (aStrLen <= MAX_IP_ADDR_LEN) && (aStrLen > 0) ) + { + //strncpy(oIP,ipAddressBuff,aStrLen); + DBG("IP address is %s \n",ipAddressBuff); + aRet = AVAHI_STATUS_OK; + } + } + return aRet; +} diff --git a/protocol/discovery/avahiDiscovery.h b/protocol/discovery/avahiDiscovery.h new file mode 100644 index 000000000..2bc3ce292 --- /dev/null +++ b/protocol/discovery/avahiDiscovery.h @@ -0,0 +1,88 @@ +#ifndef _DISCOVERY_AVAHI_H +#define _DISCOVERY_AVAHI_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <ctype.h> +#include <stdbool.h> +#include <syslog.h> +#include <stdarg.h> + +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-common/simple-watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +#ifndef DBUS_TIMEOUT_INFINITE +#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff) +#endif + +#define NULL_CHAR_SIZE 1 +#define HP_PLAIN_URI_LEN 24 +#define HP_SKIP_MFG_NAME_SIZE 3 +#define MFG_HP "HP" +#define MFG_HP_LEN 2 +#define MFG_NAME "usb_MFG" +#define MDL_NAME "mdl" +#define TYPE_NAME "ty" +#define HPMUD_LINE_SIZE 256 +#define MAX_URI_LEN 512 +#define HP_MAX_SCAN_BUFF 16384 +#define HP_EXT_SCAN_BUFF 8192 +#define MAX_NAME_LENGTH 256 +#define AVAHI_NET_DISCOVERY 1 +#define AVAHI_HOST_LOOKUP 2 +#define MAX_IP_ADDR_LEN 16 +#define APPEND_LOCAL_LEN 6 + +//Error Codes +#define AVAHI_STATUS_OK 0 +#define AVAHI_STATUS_ERROR 1 +#define AVAHI_STATUS_TIMEOUT 2 +#define SYSTEMD_SERVICE_TIMEOUT 10000 + +#define POLKIT_AUTH_DBUS "org.freedesktop.PolicyKit1" +#define POLKIT_AUTH_INTF "org.freedesktop.PolicyKit1.Authority" +#define POLKIT_AUTH_PATH "/org/freedesktop/PolicyKit1/Authority" +#define POLKIT_AUTH_METHOD_CALL "CheckAuthorization" + +#define ACTION_ID "org.freedesktop.systemd1.manage-units" +#define SYSTEM_BUS_NAME "system-bus-name" +#define AVAHI_SERVICE_NAME "avahi-daemon.service" +#define AVHHI_SERVICE_MODE_REPLACE "replace" + +#define SYSTEMD_DBUS_NAME "org.freedesktop.systemd1" +#define SYSTEMD_DBUS_PATH "/org/freedesktop/systemd1" +#define SYSTEMD_DBUS_INTF "org.freedesktop.systemd1.Manager" +#define SYSTEMD_START_SERVICE_METHOD "StartUnit" + +extern char* aUriBuf; +extern char ipAddressBuff[MAX_IP_ADDR_LEN]; + +//#define MDNS_DEBUG + +#define _STRINGIZE(x) #x +#define STRINGIZE(x) _STRINGIZE(x) + +#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args) +#ifdef AVAHI_DEBUG + #define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args) +#else + #define DBG(args...) +#endif + +/*Function Prototypes*/ + +int avahi_probe_nw_scanners(); +int avahi_lookup(const char* hostname); + +#endif // _DISCOVERY_AVAHI_H + diff --git a/protocol/discovery/mdns.c b/protocol/discovery/mdns.c deleted file mode 100644 index abca295af..000000000 --- a/protocol/discovery/mdns.c +++ /dev/null @@ -1,496 +0,0 @@ -/***************************************************************************** - mdns.c - mDNS related calls - - (c) 2015 Copyright HP Development Company, LP - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - Client/Server generic message format (see messaging-protocol.doc): - - Author: Sanjay Kumar - \*****************************************************************************/ - -//#include <stdio.h> -#include <string.h> -#include <syslog.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> -#include <arpa/inet.h> -#include "mdns.h" - -/* Convert "www.google.com" to "3www6google3com". */ -static int mdns_convert_name_to_dns(const char *name, int name_size, char *dns_name) -{ - int i, x = 0; - char *p = dns_name; - - if (name == 0 || name[0] == 0) - return 0; - - for (i = 0; i < name_size && name[i]; i++) - { - if (name[i] == '.') - { - *p++ = i - x; /* length */ - for (; x < i; x++) - *p++ = name[x]; - x++; - } - } - - if (i) - { - *p++ = i - x; /* length */ - for (; x < i; x++) - *p++ = name[x]; - x++; - } - - p[x++] = 0; - - return x; /* return length DOES include null termination */ -} - - -static int mdns_open_socket(int *psocket) -{ - int stat = MDNS_STATUS_ERROR; - int udp_socket = -1, yes = 1; - char loop = 0, ttl = 255; - struct sockaddr_in recv_addr , addr; - struct ip_mreq mreq; - - DBG("mdns_open_socket entry.\n"); - - if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - { - BUG("unable to create udp socket: %m\n"); - goto bugout; - } - - /* Get rid of "address already in use" error message. */ - if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) - { - BUG("unable to setsockopt: %m\n"); - goto bugout; - } - - /* Bind the socket to port and IP equal to INADDR_ANY. */ - bzero(&recv_addr, sizeof(recv_addr)); - recv_addr.sin_family = AF_INET; - recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); - recv_addr.sin_port = htons(5353); - if (bind(udp_socket, (struct sockaddr *) &recv_addr, sizeof(recv_addr)) == -1) - { - BUG("unable to bind udp socket: %m\n"); - goto bugout; - } - - /* Set multicast loopback off. */ - if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) == -1) - { - BUG("unable to setsockopt: %m\n"); - goto bugout; - } - - /* Set ttl to 255. Required by mdns. */ - if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))== -1) - { - BUG("unable to setsockopt: %m\n"); - goto bugout; - } - - /* Join the .local multicast group */ - mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) { - BUG("unable to add to multicast group: %m\n"); - close(udp_socket); - goto bugout; - } - - *psocket = udp_socket; - DBG("pSocket = [%d]: %m\n", *psocket); - stat = MDNS_STATUS_OK; - -bugout: - return stat; -} - -static void mdns_create_query_packet(char* fqdn, int query_type, char* querybuf, int *length) -{ - int n = 0; - char header[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - // ID/FLAGS/QDCNT/ANCNT/NSCNT/ARCNT - - DBG("mdns_create_query_packet.\n"); - memcpy(querybuf, header, sizeof(header)); - n = sizeof(header); - - n += mdns_convert_name_to_dns(fqdn, strlen(fqdn), querybuf + n); - querybuf[n++] = 0x00; - querybuf[n++] = query_type; - querybuf[n++] = 0x00; - querybuf[n++] = QCLASS_IN; - - //DBG_DUMP(dnsquery, n); - *length = n; -} - -static int mdns_send_query(int udp_socket, char *fqdn, int query_type) -{ - char querybuf[256] = {0,}; - int length = 0; - int stat = MDNS_STATUS_OK; - struct sockaddr_in send_addr; - - DBG("mdns_send_query entry. send socket=%d len=%d\n", udp_socket, length); - - mdns_create_query_packet(fqdn, query_type, querybuf, &length); - - bzero(&send_addr, sizeof(send_addr)); - send_addr.sin_family = AF_INET; - send_addr.sin_addr.s_addr = inet_addr("224.0.0.251"); - send_addr.sin_port = htons(5353); - if (sendto(udp_socket, querybuf, length, 0, (struct sockaddr *) &send_addr, sizeof(send_addr)) < 0) - stat = MDNS_STATUS_ERROR; - - DBG("mdns_send_query returning with status(%d)...\n", stat); - return stat; -} - -static int mdns_readName(unsigned char* start, unsigned char *Response, char *buf) -{ - int size = 0; - char *name = buf; - unsigned char *p = Response; - - while (size = *p++) - { - if (size >= 0xC0) - { - //Compressed Size. Just ignore it. - p++; //skip Offset byte - return (p - Response); - } - memcpy(name, p, size); - name[size] = '.'; - p += size; - name += size + 1; - } - - *(name - 1) = '\0'; - - DBG("Name = [%s]\n", buf); - return (p - Response); -} - - -static unsigned char* mdns_readMDL(unsigned char *p, unsigned char *normalized_mdl, int len) -{ - int i = 0; - int j = 0; - int z = 0; - int size = 0; - - unsigned char* mdl = normalized_mdl; - while (i < len) - { - size = *p++; - i += size + 1; - - if (strncmp(p, "mdl=", 4) == 0) - { - z = 4; - } - else if (strncmp(p, "ty=", 3) == 0) - { - z = 3+3; - } - - if(z > 0) - { - for (j = z; j < size; j++) - { - if (*(p + j) == ' ') - *mdl++ = '_'; //Replace white space with underscore - else - *mdl++ = tolower(*(p + j)); - } - - *mdl++ = '\0'; - break; - } - p += size; - - } - DBG("MDL = [%s]\n", normalized_mdl); - return p + 4; -} - -static void mdns_read_header(char *Response, DNS_PKT_HEADER *h) -{ - h->id = Response[0] << 8 | Response[1]; - h->flags = Response[2] << 8 | Response[3]; - h->questions = Response[4] << 8 | Response[5]; - h->answers = Response[6] << 8 | Response[7]; - h->authorities = Response[8] << 8 | Response[9]; - h->additionals = Response[10]<< 8 | Response[11]; - - DBG("ID=%x flags=%x Q=%x A=%x AUTH=%x ADD=%x\n", h->id, h->flags, h->questions, - h->answers, h->authorities, h->additionals); - -} - -static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr) -{ - unsigned char *p = Response; - unsigned short type = 0, data_len = 0; - DNS_PKT_HEADER h; - int i = 0; - - DBG("mdns_parse_respponse entry.\n"); - mdns_read_header(Response, &h); - p += MDNS_HEADER_SIZE; - - for (i = 0; i < h.questions; i++) - { - p += mdns_readName(Response, p, rr->name); - p += 4; //Skip TYPE(2 bytes)/CLASS(2 bytes) - } - - for (i = 0; i < (h.answers + h.additionals); i++) - { - p += mdns_readName(Response, p, rr->name); - type = (*p << 8 | *(p+1)); - p += 8; //Skip type(2 bytes)/class(2 bytes)/TTL(4 bytes) - - data_len = ( *p << 8 | *(p+1)); - p += 2; //Skip data_len(2 bytes) - - switch (type) - { - case QTYPE_A: - sprintf(rr->ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - break; - case QTYPE_TXT: - mdns_readMDL(p, rr->mdl, data_len); - break; - default: - break; - } - - p += data_len; - //DBG("TYPE = %d, Length = %d\n",type, data_len); - } - - DBG("mdns_parse_respponse returning MDL = %s, IP = %s\n",rr->mdl, rr->ip); -} - -static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvbufsize) -{ - struct timeval tmo; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - fd_set master, readfd; - int len = 0, maxfd = 0, ret = 0; - - DBG("mdns_read_single_response.\n"); - FD_ZERO(&master); - FD_SET(udp_socket, &master); - maxfd = udp_socket; - tmo.tv_sec = 0; - tmo.tv_usec = 300000; - - readfd = master; - ret = select(maxfd + 1, &readfd, NULL, NULL, &tmo); - if (ret > 0) - { - bzero(&addr, sizeof(addr)); - if ((len = recvfrom(udp_socket, recvbuffer, recvbufsize, 0, (struct sockaddr *) &addr, &addrlen)) < 0) - { - BUG("recvfrom error: (%m)\n"); - ret = -1; - } - } - - DBG("mdns_read_single_response exiting with ret = %d\n", ret); - return ret; -} - -static DNS_RECORD *mdns_read_responses(int udp_socket, int mode) -{ - int retries = 3, ret = 0; - char recvbuffer[MAX_MDNS_RESPONSE_LEN] = { 0, }; - DNS_RECORD *rr = NULL, *head = NULL, *temp = NULL; - - DBG("mdns_read_responses.\n"); - while (1 ) - { - memset(recvbuffer, 0, sizeof(recvbuffer)); - ret = mdns_read_single_response(udp_socket, recvbuffer, sizeof(recvbuffer)); - if (ret <= 0) - { - if (ret == 0 && retries--) //READ TIMEOUT. Retry few more times. - continue; - else - break; - } - else - { - temp = (DNS_RECORD *)malloc(sizeof(DNS_RECORD)); - if(temp) - { - temp->next = NULL; - if(head == NULL) - rr = head = temp; - else - { - rr->next = temp; - rr = rr->next; - } - - memset(rr, 0, sizeof(DNS_RECORD)); - mdns_parse_respponse(recvbuffer, rr); - - if(mode == MODE_READ_SINGLE) - break; - } - } - } // while(1) - - DBG("mdns_read_responses returning with (%p).\n", head); - return head; -} - -static int mdns_update_uris(DNS_RECORD *rr, char* uris_buf, int buf_size, int *count) -{ - char tempuri[MAX_URI_LEN] = {0}; - int bytes_read = 0; - - DBG("mdns_update_uris.\n"); - - *count = 0; - memset(uris_buf, 0, buf_size); - - while(rr) - { - if (rr->mdl[0] && rr->ip[0] /*&& strstr(rr->mdl, "scanjet")*/) - { - memset(tempuri, 0, sizeof(tempuri)); - sprintf(tempuri, "hp:/net/%s?ip=%s&queue=false", rr->mdl, rr->ip); - - //Check whether buffer has enough space to add new URI and check for duplicate URIs. - if(bytes_read + sizeof(tempuri) < buf_size && !strstr(uris_buf, tempuri)) - { - bytes_read += sprintf(uris_buf + bytes_read, "%s;", tempuri); - (*count)++; - *(uris_buf + bytes_read) = '\0'; - } - } - rr = rr->next; - } - - DBG("mdns_update_uris Count=[%d] bytes=[%d] URIs = %s\n",*count, bytes_read, uris_buf); - return bytes_read; -} - -static void mdns_rr_cleanup(DNS_RECORD *rr) -{ - DNS_RECORD *temp = NULL; - - DBG("mdns_rr_cleanup entry.\n"); - while(rr) - { - temp = rr->next; - free(rr); - rr = temp; - } -} - -int mdns_probe_nw_scanners(char* uris_buf, int buf_size, int *count) -{ - int n = 0, bytes_read = 0; - int udp_socket = 0; - int stat = MDNS_STATUS_ERROR; - DNS_RECORD *rr_list = NULL; - - DBG("mdns_probe_nw_scanners entry.\n"); - /* Open UDP socket */ - if (mdns_open_socket(&udp_socket) != MDNS_STATUS_OK) - goto bugout; - - /* Send dns query */ - mdns_send_query(udp_socket, "_scanner._tcp.local", QTYPE_PTR); - mdns_send_query(udp_socket, "_uscan._tcp.local", QTYPE_PTR); - - /* Read Responses */ - rr_list = mdns_read_responses(udp_socket, MODE_READ_ALL); - - /* Update URIs buffer */ - bytes_read = mdns_update_uris(rr_list, uris_buf, buf_size, count); - DBG("mdns_probe_nw_scanners returned with bytes_read = [%d].\n",bytes_read); - -bugout: - if (udp_socket >= 0) - close(udp_socket); - - mdns_rr_cleanup(rr_list); - - return bytes_read; -} - -/* - * Lookup IP for MDNS host name. - * MDNS host name example: "npi7c8a3e" (LaserJet p2055dn) - */ -int mdns_lookup(char* hostname, unsigned char* ip) -{ - int udp_socket = 0; - int stat = MDNS_STATUS_ERROR; - char fqdn[MAX_NAME_LENGTH] = {0}; - DNS_RECORD *rr_list = NULL; - - DBG("mdns_probe_nw_scanners entry.\n"); - /* Open UDP socket */ - if (mdns_open_socket(&udp_socket) != MDNS_STATUS_OK) - goto bugout; - - /* Send dns query */ - sprintf(fqdn, "%s.local", hostname); - mdns_send_query(udp_socket, fqdn, QTYPE_A); - - /* Read Responses */ - rr_list = mdns_read_responses(udp_socket, MODE_READ_SINGLE); - - /* Update IP Address buffer */ - if(rr_list) - { - strcpy(ip, rr_list->ip); - stat = MDNS_STATUS_OK; - DBG("IP = [%s].\n",ip); - } - -bugout: - if (udp_socket >= 0) - close(udp_socket); - - mdns_rr_cleanup(rr_list); - return stat; -} - diff --git a/protocol/discovery/mdns.h b/protocol/discovery/mdns.h deleted file mode 100644 index 8fccc82e2..000000000 --- a/protocol/discovery/mdns.h +++ /dev/null @@ -1,104 +0,0 @@ -/*****************************************************************************\ - - mdns.h - mDNS related calls - - (c) 2015 Copyright HP Development Company, LP - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - Client/Server generic message format (see messaging-protocol.doc): - - Author: Sanjay Kumar -\*****************************************************************************/ - -#ifndef _DISCOVERY_MDNS_H -#define _DISCOVERY_MDNS_H - -//MDNS Packet fields -#define QTYPE_A 1 -#define QTYPE_TXT 16 -#define QTYPE_SRV 33 -#define QTYPE_AAAA 28 -#define QTYPE_PTR 12 -#define QCLASS_IN 1 -#define MDNS_HEADER_SIZE 12 - -//Error Codes -#define MDNS_STATUS_OK 0 -#define MDNS_STATUS_ERROR 1 -#define MDNS_STATUS_TIMEOUT 2 - -#define MAX_IP_ADDR_LEN 16 -#define MAX_URI_LEN 256 -#define MAX_MDL_NAME_LEN 256 -#define MAX_NAME_LENGTH 256 -#define MAX_MDNS_RESPONSE_LEN 2048 -#define MODE_READ_ALL 0 -#define MODE_READ_SINGLE 1 - -/*Relevant MDNS Resource Record(RR) fields */ -typedef struct _DNS_RECORD -{ - char ip[MAX_IP_ADDR_LEN]; - char mdl[MAX_MDL_NAME_LEN]; - char name[MAX_MDL_NAME_LEN]; - struct _DNS_RECORD *next; -}DNS_RECORD; - -typedef struct _DNS_PKT_HEADER -{ - unsigned short id; - unsigned short flags; - unsigned short questions; - unsigned short answers; - unsigned short authorities; - unsigned short additionals; -}DNS_PKT_HEADER; - - -//#define MDNS_DEBUG - -#define _STRINGIZE(x) #x -#define STRINGIZE(x) _STRINGIZE(x) - -#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args) -#ifdef MDNS_DEBUG - #define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args) -#else - #define DBG(args...) -#endif - -/*Function Prototypes*/ -int mdns_probe_nw_scanners(char* buf, int buf_size, int *count); -int mdns_lookup(char* hostname, unsigned char* ip); - - -/*Helper Function Prototypes*/ -static int mdns_convert_name_to_dns(const char *name, int name_size, char *dns_name); -static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvbufsize); -static int mdns_open_socket(int *psocket); -static int mdns_send_query(int udp_socket, char *fqdn, int query_type); -static int mdns_readName(unsigned char* start, unsigned char *p, char *buf); -static int mdns_update_uris(DNS_RECORD *rr, char* uris_buf, int buf_size, int *count); -static void mdns_create_query_packet(char* fqdn, int query_type, char* dnsquery, int *length); -static void mdns_read_header(char *Response, DNS_PKT_HEADER *h); -static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr); -static void mdns_rr_cleanup(DNS_RECORD *rr); -static DNS_RECORD *mdns_read_responses(int udp_socket, int mode); -static unsigned char* mdns_readMDL(unsigned char *p, unsigned char *normalized_mdl, int len); -#endif // _DISCOVERY_MDNS_H - diff --git a/protocol/hp_ipp.c b/protocol/hp_ipp.c index 24b6b5544..af7013bf7 100644 --- a/protocol/hp_ipp.c +++ b/protocol/hp_ipp.c @@ -586,9 +586,9 @@ void initializeIPPRequest(ipp_t *request) /* * 'createDeviceStatusRequest()' - Create IPP request and update the same with values needed for getting device status attributes. */ -ipp_t * createDeviceStatusRequest() +ipp_t * createDeviceStatusRequest(const char *printer_name) { - + char uri[ HTTP_MAX_URI ] = {0}; ipp_t *request = NULL; /* IPP request object */ static const char * attrs[] = /* Requested attributes */ { @@ -605,7 +605,8 @@ ipp_t * createDeviceStatusRequest() initializeIPPRequest(request); if (request) { - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, ""); + snprintf(uri, HTTP_MAX_URI, "ipp://localhost/printers/%s", printer_name); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddStrings( request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof( attrs ) / sizeof( attrs[ 0 ] ), NULL, attrs ); } @@ -693,7 +694,7 @@ HPIPP_RESULT parseResponseHeader(char *header, int *content_length, int *chunked * marker-names, marker-types, marker-levels, marker-low-levels, printer-state, printer-state-reasons. * In addition to this it also updates the attribute count in the response. */ -ipp_t * getDeviceStatusAttributes(char* device_uri, int *count) +ipp_t * getDeviceStatusAttributes(char* device_uri,char* printer_name, int *count) { ipp_t *request = NULL; /* IPP request object */ ipp_t *response = NULL; /* IPP response object */ @@ -701,7 +702,7 @@ ipp_t * getDeviceStatusAttributes(char* device_uri, int *count) int max_count = 0; //Create Device Status Request - request = createDeviceStatusRequest(); + request = createDeviceStatusRequest(printer_name); if (request == NULL) goto abort; diff --git a/protocol/hp_ipp.h b/protocol/hp_ipp.h index 3853f8432..245e798ad 100644 --- a/protocol/hp_ipp.h +++ b/protocol/hp_ipp.h @@ -157,10 +157,10 @@ static ssize_t raw_ipp_request_callback(volatile raw_ipp *raw_buffer, ipp_uchar void initializeIPPRequest(ipp_t *request); int parsePrinterAttributes(ipp_t *response, printer_t * printer_list, int size); -ipp_t * createDeviceStatusRequest(); +ipp_t * createDeviceStatusRequest(const char* printer_name); ipp_t * usbDoRequest(ipp_t *request, char* device_uri); ipp_t * networkDoRequest(ipp_t *request, char* device_uri); -ipp_t * getDeviceStatusAttributes(char* device_uri, int *count); +ipp_t * getDeviceStatusAttributes(char* device_uri,char* printer_name, int *count); int getCupsPrinters(printer_t **printer_list); HPIPP_RESULT parseResponseHeader(char* header, int *content_length, int *chunked, int* header_size); |