1 /*
2  * epsonds-net.c - SANE library for Epson scanners.
3  *
4  * Copyright (C) 2006-2016 Tower Technologies
5  * Author: Alessandro Zummo <a.zummo@towertech.it>
6  *
7  * This file is part of the SANE package.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation, version 2.
12  */
13 
14 #define DEBUG_DECLARE_ONLY
15 
16 #include "sane/config.h"
17 
18 #ifdef HAVE_SYS_SELECT_H
19 #include <sys/select.h>
20 #endif
21 
22 #include "sane/sane.h"
23 #include "sane/saneopts.h"
24 #include "sane/sanei_tcp.h"
25 #include "sane/sanei_config.h"
26 #include "sane/sanei_backend.h"
27 
28 #include "epsonds.h"
29 #include "epsonds-net.h"
30 
31 #include "byteorder.h"
32 
33 #include "sane/sanei_debug.h"
34 
35 
36 static ssize_t
epsonds_net_read_raw(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status *status)37 epsonds_net_read_raw(epsonds_scanner *s, unsigned char *buf, ssize_t wanted,
38 		       SANE_Status *status)
39 {
40 	DBG(15, "%s: wanted: %ld\n", __func__, wanted);
41 
42 	if (wanted == 0)
43 	{
44 	    *status = SANE_STATUS_GOOD;
45 		return 0;
46 	}
47 
48 	int ready;
49 	ssize_t read = -1;
50 	fd_set readable;
51 	struct timeval tv;
52 
53 	tv.tv_sec = 10;
54 	tv.tv_usec = 0;
55 
56 	FD_ZERO(&readable);
57 	FD_SET(s->fd, &readable);
58 
59 	ready = select(s->fd + 1, &readable, NULL, NULL, &tv);
60 	if (ready > 0) {
61 		read = sanei_tcp_read(s->fd, buf, wanted);
62 	} else {
63 		DBG(15, "%s: select failed: %d\n", __func__, ready);
64 	}
65 
66 	*status = SANE_STATUS_GOOD;
67 
68 	if (read < wanted) {
69 		*status = SANE_STATUS_IO_ERROR;
70 	}
71 
72 	return read;
73 }
74 
75 static ssize_t
epsonds_net_read_buf(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status)76 epsonds_net_read_buf(epsonds_scanner *s, unsigned char *buf, ssize_t wanted,
77 		       SANE_Status * status)
78 {
79 	ssize_t read = 0;
80 
81 	DBG(23, "%s: reading up to %lu from buffer at %p, %lu available\n",
82 		__func__, (u_long) wanted, (void *) s->netptr, (u_long) s->netlen);
83 
84 	if ((size_t) wanted > s->netlen) {
85 		*status = SANE_STATUS_IO_ERROR;
86 		wanted = s->netlen;
87 	}
88 
89 	memcpy(buf, s->netptr, wanted);
90 	read = wanted;
91 
92 	s->netptr += read;
93 	s->netlen -= read;
94 
95 	if (s->netlen == 0) {
96 		DBG(23, "%s: freeing %p\n", __func__, (void *) s->netbuf);
97 		free(s->netbuf);
98 		s->netbuf = s->netptr = NULL;
99 		s->netlen = 0;
100 	}
101 
102 	return read;
103 }
104 
105 ssize_t
epsonds_net_read(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status)106 epsonds_net_read(epsonds_scanner *s, unsigned char *buf, ssize_t wanted,
107 		       SANE_Status * status)
108 {
109 	if (wanted < 0) {
110 		*status = SANE_STATUS_INVAL;
111 		return 0;
112 	}
113 
114 	size_t size;
115 	ssize_t read = 0;
116 	unsigned char header[12];
117 
118 	/* read from remainder of buffer */
119 	if (s->netptr) {
120 		return epsonds_net_read_buf(s, buf, wanted, status);
121 	}
122 
123 	/* receive net header */
124 	read = epsonds_net_read_raw(s, header, 12, status);
125 	if (read != 12) {
126 		return 0;
127 	}
128 
129 	/* validate header */
130 	if (header[0] != 'I' || header[1] != 'S') {
131 		DBG(1, "header mismatch: %02X %02x\n", header[0], header[1]);
132 		*status = SANE_STATUS_IO_ERROR;
133 		return 0;
134 	}
135 
136 	/* parse payload size */
137 	size = be32atoh(&header[6]);
138 
139 	*status = SANE_STATUS_GOOD;
140 
141 	if (!s->netbuf) {
142 		DBG(15, "%s: direct read\n", __func__);
143 		DBG(23, "%s: wanted = %lu, available = %lu\n", __func__,
144 			(u_long) wanted, (u_long) size);
145 
146 		if ((size_t) wanted > size) {
147 			wanted = size;
148 		}
149 
150 		read = epsonds_net_read_raw(s, buf, wanted, status);
151 	} else {
152 		DBG(15, "%s: buffered read\n", __func__);
153 		DBG(23, "%s: bufferable = %lu, available = %lu\n", __func__,
154 			(u_long) s->netlen, (u_long) size);
155 
156 		if (s->netlen > size) {
157 			s->netlen = size;
158 		}
159 
160 		/* fill buffer */
161 		read = epsonds_net_read_raw(s, s->netbuf, s->netlen, status);
162 		s->netptr = s->netbuf;
163 		s->netlen = (read > 0 ? read : 0);
164 
165 		/* copy wanted part */
166 		read = epsonds_net_read_buf(s, buf, wanted, status);
167 	}
168 
169 	return read;
170 }
171 
172 SANE_Status
epsonds_net_request_read(epsonds_scanner *s, size_t len)173 epsonds_net_request_read(epsonds_scanner *s, size_t len)
174 {
175 	SANE_Status status;
176 	epsonds_net_write(s, 0x2000, NULL, 0, len, &status);
177 	return status;
178 }
179 
180 size_t
epsonds_net_write(epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status)181 epsonds_net_write(epsonds_scanner *s, unsigned int cmd, const unsigned char *buf,
182 			size_t buf_size, size_t reply_len, SANE_Status *status)
183 {
184 	unsigned char *h1, *h2;
185 	unsigned char *packet = malloc(12 + 8);
186 
187 	if (!packet) {
188 		*status = SANE_STATUS_NO_MEM;
189 		return 0;
190 	}
191 
192 	h1 = packet;		// packet header
193 	h2 = packet + 12;	// data header
194 
195 	if (reply_len) {
196 		if (s->netbuf) {
197 			DBG(23, "%s, freeing %p, %ld bytes unprocessed\n",
198 				__func__, (void *) s->netbuf, (u_long) s->netlen);
199 			free(s->netbuf);
200 			s->netbuf = s->netptr = NULL;
201 			s->netlen = 0;
202 		}
203 		s->netbuf = malloc(reply_len);
204 		if (!s->netbuf) {
205 			free(packet);
206 			*status = SANE_STATUS_NO_MEM;
207 			return 0;
208 		}
209 		s->netlen = reply_len;
210 		DBG(24, "%s: allocated %lu bytes at %p\n", __func__,
211 			(u_long) s->netlen, (void *) s->netbuf);
212 	}
213 
214 	DBG(24, "%s: cmd = %04x, buf = %p, buf_size = %lu, reply_len = %lu\n",
215 		__func__, cmd, (void *) buf, (u_long) buf_size, (u_long) reply_len);
216 
217 	memset(h1, 0x00, 12);
218 	memset(h2, 0x00, 8);
219 
220 	h1[0] = 'I';
221 	h1[1] = 'S';
222 
223 	h1[2] = cmd >> 8;	// packet type
224 	h1[3] = cmd;		// data type
225 
226 	h1[4] = 0x00;
227 	h1[5] = 0x0C; // data offset
228 
229 	DBG(24, "H1[0]: %02x %02x %02x %02x\n", h1[0], h1[1], h1[2], h1[3]);
230 
231 	// 0x20 passthru
232 	// 0x21 job control
233 
234 	if (buf_size) {
235 		htobe32a(&h1[6], buf_size);
236 	}
237 
238 	if((cmd >> 8) == 0x20) {
239 
240 		htobe32a(&h1[6], buf_size + 8);	// data size (data header + payload)
241 
242 		htobe32a(&h2[0], buf_size);	// payload size
243 		htobe32a(&h2[4], reply_len);	// expected answer size
244 
245 		DBG(24, "H1[6]: %02x %02x %02x %02x (%lu)\n", h1[6], h1[7], h1[8], h1[9], (u_long) (buf_size + 8));
246 		DBG(24, "H2[0]: %02x %02x %02x %02x (%lu)\n", h2[0], h2[1], h2[2], h2[3], (u_long) buf_size);
247 		DBG(24, "H2[4]: %02x %02x %02x %02x (%lu)\n", h2[4], h2[5], h2[6], h2[7], (u_long) reply_len);
248 	}
249 
250 	if ((cmd >> 8) == 0x20 && (buf_size || reply_len)) {
251 
252 		// send header + data header
253 		sanei_tcp_write(s->fd, packet, 12 + 8);
254 
255 	} else {
256 		sanei_tcp_write(s->fd, packet, 12);
257 	}
258 
259 	// send payload
260 	if (buf_size)
261 		sanei_tcp_write(s->fd, buf, buf_size);
262 
263 	free(packet);
264 
265 	*status = SANE_STATUS_GOOD;
266 	return buf_size;
267 }
268 
269 SANE_Status
epsonds_net_lock(struct epsonds_scanner *s)270 epsonds_net_lock(struct epsonds_scanner *s)
271 {
272 	SANE_Status status;
273 	unsigned char buf[7] = "\x01\xa0\x04\x00\x00\x01\x2c";
274 
275 	DBG(1, "%s\n", __func__);
276 
277 	epsonds_net_write(s, 0x2100, buf, 7, 0, &status);
278 	epsonds_net_read(s, buf, 1, &status);
279 
280 	// buf[0] should be ACK, 0x06
281 
282 	return status;
283 }
284 
285 SANE_Status
epsonds_net_unlock(struct epsonds_scanner *s)286 epsonds_net_unlock(struct epsonds_scanner *s)
287 {
288 	SANE_Status status;
289 
290 	DBG(1, "%s\n", __func__);
291 
292 	epsonds_net_write(s, 0x2101, NULL, 0, 0, &status);
293 /*	epsonds_net_read(s, buf, 1, &status); */
294 	return status;
295 }
296 #if WITH_AVAHI
297 
298 #include <assert.h>
299 #include <stdio.h>
300 #include <stdlib.h>
301 #include <string.h>
302 #include <avahi-client/lookup.h>
303 #include <avahi-common/error.h>
304 #include <avahi-common/simple-watch.h>
305 #include <sys/time.h>
306 #include <errno.h>
307 
308 static AvahiSimplePoll *simple_poll = NULL;
309 
310 static struct timeval borowseEndTime;
311 
312 static int resolvedCount = 0;
313 static int browsedCount = 0;
314 static int waitResolver = 0;
315 
316 typedef struct {
317     AvahiClient* client;
318     Device_Found_CallBack callBack;
319 }EDSAvahiUserData;
320 
my_avahi_simple_poll_loop(AvahiSimplePoll *s)321 static int my_avahi_simple_poll_loop(AvahiSimplePoll *s) {
322     struct timeval currentTime;
323 
324     for (;;)
325     {
326          int r = avahi_simple_poll_iterate(s, 1);
327 		if (r != 0)
328 		{
329 			if (r >= 0 || errno != EINTR)
330 			{
331 					DBG(10, "my_avahi_simple_poll_loop end\n");
332 					return r;
333 			}
334 		}
335 
336 		if (waitResolver) {
337 			gettimeofday(&currentTime, NULL);
338 
339 			if ((currentTime.tv_sec - borowseEndTime.tv_sec) >= 3)
340 			{
341 				avahi_simple_poll_quit(simple_poll);
342 				DBG(10, "resolve timeout\n");
343 				return 0;
344 			}
345 		}
346     }
347 }
348 
349 static void
epsonds_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, void *userdata)350 epsonds_resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface,
351                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
352                             AvahiResolverEvent event, const char *name,
353                             const char  *type,
354                             const char  *domain,
355                             const char  *host_name,
356                             const AvahiAddress *address, uint16_t port, AvahiStringList *txt,
357                             AvahiLookupResultFlags  flags,
358                             void  *userdata)
359 {
360 	// unused parameter
361 	(void)r;
362 	(void)type;
363 	(void)domain;
364 	(void)host_name;
365 	(void)port;
366 	(void)flags;
367     EDSAvahiUserData* data = userdata;
368     char ipAddr[AVAHI_ADDRESS_STR_MAX];
369 
370 	DBG(10, "epsonds_searchDevices resolve_callback\n");
371 
372 
373     resolvedCount++;
374 
375     switch (event) {
376     case AVAHI_RESOLVER_FAILURE:
377         break;
378     case AVAHI_RESOLVER_FOUND:
379         avahi_address_snprint(ipAddr, sizeof(ipAddr), address);
380 	   DBG(10, "epsonds_searchDevices name = %s \n", name);
381         if (strlen(name) > 7)
382         {
383             if (strncmp(name, "EPSON", 5) == 0)
384             {
385 				while(txt != NULL)
386 				{
387 					char* text = (char*)avahi_string_list_get_text(txt);
388 					DBG(10, "avahi string = %s\n", text);
389 
390 					if (strlen(text) > 4 && strncmp(text, "mdl=", 4) == 0)
391 					{
392 						if (data->callBack)
393                 		{
394 							data->callBack(&text[4], ipAddr);
395 							break;
396                 		}
397 					}
398 					txt = avahi_string_list_get_next(txt);
399 				}
400 
401             }
402         }
403 		break;
404     }
405 }
406 
407 static void
browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata)408 browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
409                             AvahiProtocol protocol, AvahiBrowserEvent event,
410                             const char *name, const char *type,
411                             const char *domain,
412                             AvahiLookupResultFlags flags,
413                             void* userdata)
414 {
415     DBG(10, "browse_callback event = %d\n", event);
416 
417 	//unused parameter
418 	(void)b;
419 	(void)flags;
420 
421     EDSAvahiUserData *data = userdata;
422     switch (event) {
423     case AVAHI_BROWSER_FAILURE:
424         avahi_simple_poll_quit(simple_poll);
425         return;
426     case AVAHI_BROWSER_NEW:
427 	    DBG(10, "browse_callback name = %s\n", name);
428 		browsedCount++;
429         if (!(avahi_service_resolver_new(data->client, interface, protocol, name,
430                                                                type, domain,
431                                                                AVAHI_PROTO_UNSPEC, 0,
432                                                                epsonds_resolve_callback, data)))
433 		{
434 			DBG(10, "avahi_service_resolver_new fails\n");
435 		    break;
436 		}
437     case AVAHI_BROWSER_REMOVE:
438         break;
439     case AVAHI_BROWSER_ALL_FOR_NOW:
440 		DBG(10, "AVAHI_BROWSER_ALL_FOR_NOW\n");
441         gettimeofday(&borowseEndTime, NULL);
442 
443         if (browsedCount > resolvedCount)
444         {
445 			  DBG(10, "WAIT RESOLVER\n");
446                waitResolver = 1;
447          }else{
448 			 DBG(10, "QUIT POLL\n");
449              avahi_simple_poll_quit(simple_poll);
450          }
451 		break;
452     case AVAHI_BROWSER_CACHE_EXHAUSTED:
453 		 DBG(10, "AVAHI_BROWSER_CACHE_EXHAUSTED\n");
454         break;
455     }
456 }
457 
458 static void
client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata)459 client_callback(AvahiClient *c, AvahiClientState state,
460                          AVAHI_GCC_UNUSED void *userdata)
461 {
462     assert(c);
463     if (state == AVAHI_CLIENT_FAILURE)
464         avahi_simple_poll_quit(simple_poll);
465 }
466 
epsonds_searchDevices(Device_Found_CallBack deviceFoundCallBack)467 SANE_Status epsonds_searchDevices(Device_Found_CallBack deviceFoundCallBack)
468 {
469 	int result = SANE_STATUS_GOOD;
470 
471     AvahiClient *client = NULL;
472     AvahiServiceBrowser *sb = NULL;
473 
474     EDSAvahiUserData data;
475 
476     resolvedCount = 0;
477 	browsedCount = 0;
478 	waitResolver = 0;
479 
480 
481 	int error = 0;
482     DBG(10, "epsonds_searchDevices\n");
483 
484     if (!(simple_poll = avahi_simple_poll_new())) {
485         DBG(10, "avahi_simple_poll_new failed\n");
486 		result = SANE_STATUS_INVAL;
487         goto fail;
488     }
489     client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0,
490                                                client_callback, NULL, &error);
491     if (!client) {
492         DBG(10, "avahi_client_new failed %s\n", avahi_strerror(error));
493 		result = SANE_STATUS_INVAL;
494         goto fail;
495     }
496     data.client = client;
497     data.callBack = deviceFoundCallBack;
498 
499     if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
500                                                                    AVAHI_PROTO_UNSPEC, "_scanner._tcp",
501                                                                    NULL, 0, browse_callback, &data))) {
502         DBG(10, "avahi_service_browser_new failed: %s\n",
503                               avahi_strerror(avahi_client_errno(client)));
504 		result = SANE_STATUS_INVAL;
505         goto fail;
506     }
507     my_avahi_simple_poll_loop(simple_poll);
508 fail:
509     if (sb)
510         avahi_service_browser_free(sb);
511     if (client)
512         avahi_client_free(client);
513     if (simple_poll)
514         avahi_simple_poll_free(simple_poll);
515 
516     DBG(10, "epsonds_searchDevices fin\n");
517 
518     return result;
519 }
520 #endif
521