MagickCore 7.1.2-35
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
distribute-cache.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% DDDD IIIII SSSSS TTTTT RRRR IIIII BBBB U U TTTTT EEEEE %
6% D D I SS T R R I B B U U T E %
7% D D I SSS T RRRR I BBBB U U T EEE %
8% D D I SS T R R I B B U U T E %
9% DDDDA IIIII SSSSS T R R IIIII BBBB UUU T EEEEE %
10% %
11% CCCC AAA CCCC H H EEEEE %
12% C A A C H H E %
13% C AAAAA C HHHHH EEE %
14% C A A C H H E %
15% CCCC A A CCCC H H EEEEE %
16% %
17% %
18% MagickCore Distributed Pixel Cache Methods %
19% %
20% Software Design %
21% Cristy %
22% January 2013 %
23% %
24% %
25% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
26% dedicated to making software imaging solutions freely available. %
27% %
28% You may not use this file except in compliance with the License. You may %
29% obtain a copy of the License at %
30% %
31% https://imagemagick.org/license/ %
32% %
33% Unless required by applicable law or agreed to in writing, software %
34% distributed under the License is distributed on an "AS IS" BASIS, %
35% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
36% See the License for the specific language governing permissions and %
37% limitations under the License. %
38% %
39%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40%
41% A distributed pixel cache is an extension of the traditional pixel cache
42% available on a single host. The distributed pixel cache may span multiple
43% servers so that it can grow in size and transactional capacity to support
44% very large images. Start up the pixel cache server on one or more machines.
45% When you read or operate on an image and the local pixel cache resources are
46% exhausted, ImageMagick contacts one or more of these remote pixel servers to
47% store or retrieve pixels.
48%
49*/
50␌
51/*
52 Include declarations.
53*/
54#include "MagickCore/studio.h"
55#include "MagickCore/cache.h"
56#include "MagickCore/cache-private.h"
57#include "MagickCore/distribute-cache.h"
58#include "MagickCore/distribute-cache-private.h"
59#include "MagickCore/exception.h"
60#include "MagickCore/exception-private.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/locale_.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/nt-base-private.h"
68#include "MagickCore/pixel.h"
69#include "MagickCore/policy.h"
70#include "MagickCore/random_.h"
71#include "MagickCore/registry.h"
72#include "MagickCore/splay-tree.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/string-private.h"
75#include "MagickCore/utility-private.h"
76#include "MagickCore/version.h"
77#include "MagickCore/version-private.h"
78#define SOCKET_TYPE int
79#undef MAGICKCORE_HAVE_DISTRIBUTE_CACHE
80#if defined(MAGICKCORE_DPC_SUPPORT)
81#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
82#include <netinet/in.h>
83#include <netinet/tcp.h>
84#include <netdb.h>
85#include <sys/socket.h>
86#include <arpa/inet.h>
87#define CLOSE_SOCKET(socket) (void) close_utf8(socket)
88#define HANDLER_RETURN_TYPE void *
89#define HANDLER_RETURN_VALUE (void *) NULL
90#define SOCKET_TYPE int
91#define LENGTH_TYPE size_t
92#define MAGICKCORE_HAVE_DISTRIBUTE_CACHE 1
93#elif defined(_MSC_VER)
94#define CLOSE_SOCKET(socket) (void) closesocket(socket)
95#define HANDLER_RETURN_TYPE DWORD WINAPI
96#define HANDLER_RETURN_VALUE 0
97#define LENGTH_TYPE int
98#define MAGICKCORE_HAVE_DISTRIBUTE_CACHE 1
99#define MAGICKCORE_HAVE_WINSOCK2 1
100#endif
101#endif
102␌
103/*
104 Define declarations.
105*/
106#define DPCHostname "127.0.0.1"
107#define DPCMacLength 32
108#define DPCMaxClientWorkers 128
109#define DPCMaxUnauthenticatedClientWorkers 16
110#define DPCPendingConnections 10
111#define DPCPort 6668
112#define DPCScratchExtent 16384 /* bytes; send-side encrypt buffer */
113#define DPCSessionKeyLength 16
114#ifndef MSG_NOSIGNAL
115# define MSG_NOSIGNAL 0
116#endif
117␌
118/*
119 Typedef declarations.
120*/
121typedef struct
122{
123 uint32_t
124 state[8];
125
126 uint64_t
127 count;
128
129 unsigned char
130 buffer[64];
131} SHA256Hash;
132␌
133/*
134 Static declarations.
135*/
136#ifdef MAGICKCORE_HAVE_WINSOCK2
137static SemaphoreInfo
138 *winsock_semaphore = (SemaphoreInfo *) NULL;
139
140static WSADATA
141 *wsaData = (WSADATA*) NULL;
142#endif
143
144#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
145static SemaphoreInfo
146 *dpc_semaphore = (SemaphoreInfo *) NULL;
147
148static char
149 *dpc_shared_secret = (char *) NULL;
150
151static size_t
152 dpc_clients = 0,
153 dpc_unauthenticated_clients = 0;
154#endif
155␌
156/*
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158% %
159% %
160% %
161+ A c q u i r e D i s t r i b u t e C a c h e I n f o %
162% %
163% %
164% %
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166%
167% AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
168%
169% The format of the AcquireDistributeCacheInfo method is:
170%
171% DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
172%
173% A description of each parameter follows:
174%
175% o exception: return any errors or warnings in this structure.
176%
177*/
178
179#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
180static inline MagickOffsetType dpc_receive(SOCKET_TYPE magick_unused(file),
181 const MagickSizeType magick_unused(length),
182 unsigned char *magick_restrict magick_unused(message))
183{
184 magick_unreferenced(file);
185 magick_unreferenced(length);
186 magick_unreferenced(message);
187 return(-1);
188}
189#else
190static void SetSocketNoDelay(SOCKET_TYPE file)
191{
192 int
193 one = 1;
194
195 /*
196 Disable Nagle.
197 */
198 (void) setsockopt(file,IPPROTO_TCP,TCP_NODELAY,(const char *) &one,
199 (socklen_t) sizeof(one));
200}
201
202static void SetSocketReceiveTimeout(SOCKET_TYPE file,double seconds)
203{
204 /*
205 Set a receive timeout.
206 */
207#if defined(MAGICKCORE_HAVE_WINSOCK2)
208 {
209 DWORD timeout = (DWORD) (seconds*1000.0);
210 (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,
211 (const char *) &timeout,sizeof(timeout));
212 }
213#else
214 {
215 struct timeval tv;
216 tv.tv_sec=(int) seconds;
217 tv.tv_usec=(int) ((seconds-(double) tv.tv_sec)*1000000.0);
218 (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
219 }
220#endif
221}
222
223static inline MagickOffsetType dpc_receive(SOCKET_TYPE file,
224 const MagickSizeType length,unsigned char *magick_restrict message)
225{
226 MagickOffsetType offset = 0;
227 while (offset < (MagickOffsetType) length)
228 {
229 MagickSizeType chunk = MagickMin(length-offset,(MagickSizeType)
230 MagickMaxBufferExtent);
231 ssize_t count = recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
232 if (count > 0)
233 offset+=(MagickOffsetType) count;
234 else
235 if (count == 0)
236 return(0);
237 else
238 {
239#ifdef MAGICKCORE_HAVE_WINSOCK2
240 int wsa_err = WSAGetLastError();
241 if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
242 continue;
243 if (wsa_err == WSAECONNRESET)
244 return(-2);
245#else
246 if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
247 continue;
248 if (errno == ECONNRESET)
249 return(-2);
250#endif
251 return(-1);
252 }
253 }
254 return(offset);
255}
256
257static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE file,
258 MagickSizeType length,unsigned char *message,double deadline)
259{
260 /*
261 Enforce an absolute deadline for authentication and command reads.
262 */
263 MagickOffsetType offset = 0;
264 while (offset < (MagickOffsetType) length)
265 {
266 double
267 now = (double) time((time_t *) NULL),
268 remaining = deadline-now;
269
270 MagickSizeType
271 chunk;
272
273 ssize_t
274 count;
275
276 if (now >= deadline)
277 return(-1);
278 if (remaining < 0.001)
279 remaining=0.001;
280 SetSocketReceiveTimeout(file,remaining);
281 chunk=MagickMin(length-offset,(MagickSizeType) MagickMaxBufferExtent);
282 count=recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
283 if (count > 0)
284 offset+=(MagickOffsetType) count;
285 else
286 if (count == 0)
287 return(0);
288 else
289 {
290#ifdef MAGICKCORE_HAVE_WINSOCK2
291 int wsa_err = WSAGetLastError();
292 if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
293 continue;
294 if (wsa_err == WSAECONNRESET)
295 return(-2);
296 return(-1);
297#else
298 if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
299 continue;
300 if (errno == ECONNRESET)
301 return(-2);
302 return(-1);
303#endif
304 }
305 }
306 return(offset);
307}
308#endif
309
310#if defined(MAGICKCORE_HAVE_WINSOCK2)
311static void InitializeWinsock2(MagickBooleanType use_lock)
312{
313 if (use_lock != MagickFalse)
314 {
315 if (winsock_semaphore == (SemaphoreInfo *) NULL)
316 ActivateSemaphoreInfo(&winsock_semaphore);
317 LockSemaphoreInfo(winsock_semaphore);
318 }
319 if (wsaData == (WSADATA *) NULL)
320 {
321 wsaData=(WSADATA *) AcquireMagickMemory(sizeof(WSADATA));
322 if (WSAStartup(MAKEWORD(2,2),wsaData) != 0)
323 ThrowFatalException(CacheFatalError,"WSAStartup failed");
324 }
325 if (use_lock != MagickFalse)
326 UnlockSemaphoreInfo(winsock_semaphore);
327}
328#endif
329
330#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
331static int ConnectPixelCacheServer(const char *magick_unused(hostname),
332 const int magick_unused(port),uint64_t *magick_unused(session_key),
333 ExceptionInfo *exception)
334{
335 magick_unreferenced(hostname);
336 magick_unreferenced(port);
337 magick_unreferenced(session_key);
338 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
339 "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
340 return(MagickFalse);
341}
342#else
343static inline uint64_t ROTL(uint64_t x,int b)
344{
345 return((x << b) | (x >> (64-b)));
346}
347
348static inline uint64_t U8TO64_LE(const uint8_t *p)
349{
350 return(((uint64_t) p[0] << 0) | ((uint64_t) p[1] << 8) |
351 ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) |
352 ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) |
353 ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56));
354}
355
356static inline uint64_t SIPHash24(const uint8_t key[16],const uint8_t *message,
357 const size_t length)
358{
359 const uint8_t
360 *end = message+length-(length % 8);
361
362 size_t
363 i;
364
365 uint64_t
366 b = ((uint64_t) length) << 56,
367 k0 = U8TO64_LE(key),
368 k1 = U8TO64_LE(key+8),
369 m,
370 v0 = 0x736f6d6570736575ULL ^ k0,
371 v1 = 0x646f72616e646f6dULL ^ k1,
372 v2 = 0x6c7967656e657261ULL ^ k0,
373 v3 = 0x7465646279746573ULL ^ k1;
374
375 for ( ; message != end; message+=8)
376 {
377 m=U8TO64_LE(message);
378 v3^=m;
379 for (i=0; i < 2; i++)
380 {
381 v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
382 v2+=v3; v3=ROTL(v3,16); v3^=v2;
383 v0+=v3; v3=ROTL(v3,21); v3^=v0;
384 v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
385 }
386 v0^=m;
387 }
388 switch (length & 0x07)
389 {
390 case 7: b|=((uint64_t) message[6]) << 48; magick_fallthrough;
391 case 6: b|=((uint64_t) message[5]) << 40; magick_fallthrough;
392 case 5: b|=((uint64_t) message[4]) << 32; magick_fallthrough;
393 case 4: b|=((uint64_t) message[3]) << 24; magick_fallthrough;
394 case 3: b|=((uint64_t) message[2]) << 16; magick_fallthrough;
395 case 2: b|=((uint64_t) message[1]) << 8; magick_fallthrough;
396 case 1: b|=((uint64_t) message[0]); magick_fallthrough;
397 default: break;
398 }
399 v3^=b;
400 for (i=0; i < 2; i++)
401 {
402 v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
403 v2+=v3; v3=ROTL(v3,16); v3^=v2;
404 v0+=v3; v3=ROTL(v3,21); v3^=v0;
405 v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
406 }
407 v0^=b;
408 v2^=0xff;
409 for (i=0; i < 4; i++)
410 {
411 v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
412 v2+=v3; v3=ROTL(v3,16); v3^=v2;
413 v0+=v3; v3=ROTL(v3,21); v3^=v0;
414 v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
415 }
416 return(v0^v1^v2^v3);
417}
418
419static inline void DeriveSIPKeyFromSecret(const char *shared_secret,
420 uint8_t key[16])
421{
422 size_t
423 i,
424 length;
425
426 uint64_t
427 k0 = 0x0706050403020100ULL,
428 k1 = 0x0f0e0d0c0b0a0908ULL;
429
430 length=strlen(shared_secret);
431 for (i=0; i < length; i++)
432 {
433 uint8_t
434 b = (uint8_t) shared_secret[i];
435
436 k0^=b;
437 k0*=0x100000001b3ULL;
438 k1^=(uint64_t) b << ((i & 7)*8);
439 k1=(k1 << 5) | (k1 >> (64-5));
440 }
441 (void) memcpy(key,&k0,8);
442 (void) memcpy(key+8,&k1,8);
443}
444
445static inline uint64_t GenerateSessionKey(const char *shared_secret,
446 const unsigned char *nonce,size_t length)
447{
448 uint8_t
449 key[16];
450
451 DeriveSIPKeyFromSecret(shared_secret,key);
452 return(SIPHash24(key,nonce,length));
453}
454
455static int ConnectPixelCacheServer(const char *hostname,const int port,
456 uint64_t *session_key,ExceptionInfo *exception)
457{
458#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
459 char
460 *message,
461 service[MagickPathExtent],
462 *shared_secret;
463
464 int
465 status;
466
467 SOCKET_TYPE
468 client_socket;
469
470 ssize_t
471 count;
472
473 struct addrinfo
474 hints,
475 *result;
476
477 unsigned char
478 nonce[DPCSessionKeyLength];
479
480 /*
481 Connect to distributed pixel cache server and get session key.
482 */
483 *session_key=0;
484#if defined(MAGICKCORE_HAVE_WINSOCK2)
485 InitializeWinsock2(MagickTrue);
486#endif
487 (void) memset(&hints,0,sizeof(hints));
488 hints.ai_family=AF_INET;
489 hints.ai_socktype=SOCK_STREAM;
490 hints.ai_flags=AI_PASSIVE;
491 (void) FormatLocaleString(service,MagickPathExtent,"%d",port);
492 status=getaddrinfo(hostname,service,&hints,&result);
493 if (status != 0)
494 {
495 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
496 "DistributedPixelCache","'%s': %s",hostname,gai_strerror(status));
497 return(-1);
498 }
499 client_socket=(SOCKET_TYPE) socket(result->ai_family,result->ai_socktype,
500 result->ai_protocol);
501 if (client_socket == -1)
502 {
503 freeaddrinfo(result);
504 message=GetExceptionMessage(errno);
505 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
506 "DistributedPixelCache","'%s': %s",hostname,message);
507 message=DestroyString(message);
508 return(-1);
509 }
510 status=connect(client_socket,result->ai_addr,(socklen_t) result->ai_addrlen);
511 freeaddrinfo(result);
512 if (status == -1)
513 {
514 CLOSE_SOCKET(client_socket);
515 message=GetExceptionMessage(errno);
516 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
517 "DistributedPixelCache","'%s': %s",hostname,message);
518 message=DestroyString(message);
519 return(-1);
520 }
521 SetSocketNoDelay(client_socket);
522 /*
523 Receive server nonce.
524 */
525 count=recv(client_socket,(char *) nonce,sizeof(nonce),0);
526 if (count != (ssize_t) sizeof(nonce))
527 {
528 CLOSE_SOCKET(client_socket);
529 message=GetExceptionMessage(errno);
530 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
531 "DistributedPixelCache","'%s': %s",hostname,message);
532 message=DestroyString(message);
533 return(-1);
534 }
535 /*
536 Compute keyed hash(shared_secret,nonce).
537 */
538 shared_secret=GetPolicyValue("cache:shared-secret");
539 if (shared_secret == (const char *) NULL)
540 {
541 CLOSE_SOCKET(client_socket);
542 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
543 "DistributedPixelCache","'%s': shared secret required",hostname);
544 return(-1);
545 }
546 *session_key=GenerateSessionKey(shared_secret,nonce,sizeof(nonce));
547 shared_secret=DestroyString(shared_secret);
548 /*
549 Send keyed hash back to client.
550 */
551 count=send(client_socket,(char *) session_key,sizeof(*session_key),
552 MSG_NOSIGNAL);
553 if (count != (ssize_t) sizeof(*session_key))
554 {
555 CLOSE_SOCKET(client_socket);
556 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
557 "DistributedPixelCache","'%s': authentication failed",hostname);
558 return(-1);
559 }
560 return((int) client_socket);
561#else
562 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
563 "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
564 return(-1);
565#endif
566}
567#endif
568
569static char *GetHostname(int *port,ExceptionInfo *exception)
570{
571 char
572 *host,
573 *hosts,
574 **hostlist;
575
576 int
577 argc;
578
579 ssize_t
580 i;
581
582 static size_t
583 id = 0;
584
585 /*
586 Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
587 */
588 hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",exception);
589 if (hosts == (char *) NULL)
590 {
591 *port=DPCPort;
592 return(AcquireString(DPCHostname));
593 }
594 (void) SubstituteString(&hosts,","," ");
595 hostlist=StringToArgv(hosts,&argc);
596 hosts=DestroyString(hosts);
597 if ((hostlist == (char **) NULL) || ((argc-1) == 0))
598 {
599 *port=DPCPort;
600 return(AcquireString(DPCHostname));
601 }
602 {
603 size_t host_count = (size_t) argc-1;
604 size_t index = (id++ % host_count)+1;
605 hosts=AcquireString(hostlist[index]);
606 }
607 for (i=0; i < (ssize_t) argc; i++)
608 hostlist[i]=DestroyString(hostlist[i]);
609 hostlist=(char **) RelinquishMagickMemory(hostlist);
610 (void) SubstituteString(&hosts,":"," ");
611 hostlist=StringToArgv(hosts,&argc);
612 if (hostlist == (char **) NULL)
613 {
614 *port=DPCPort;
615 return(AcquireString(DPCHostname));
616 }
617 host=AcquireString(hostlist[1]);
618 if (hostlist[2] == (char *) NULL)
619 *port=DPCPort;
620 else
621 *port=(int) StringToLong(hostlist[2]);
622 for (i=0; i < (ssize_t) argc; i++)
623 hostlist[i]=DestroyString(hostlist[i]);
624 hostlist=(char **) RelinquishMagickMemory(hostlist);
625 return(host);
626}
627
628MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
629 ExceptionInfo *exception)
630{
631 char
632 *hostname;
633
634 DistributeCacheInfo
635 *server_info;
636
637 uint64_t
638 session_key;
639
640 /*
641 Connect to the distributed pixel cache server.
642 */
643 server_info=(DistributeCacheInfo *) AcquireCriticalMemory(
644 sizeof(*server_info));
645 (void) memset(server_info,0,sizeof(*server_info));
646 server_info->signature=MagickCoreSignature;
647 server_info->port=0;
648 hostname=GetHostname(&server_info->port,exception);
649 session_key=0;
650 server_info->file=ConnectPixelCacheServer(hostname,server_info->port,
651 &session_key,exception);
652 if (server_info->file == -1)
653 server_info=DestroyDistributeCacheInfo(server_info);
654 else
655 {
656 server_info->session_key=session_key;
657 (void) CopyMagickString(server_info->hostname,hostname,MagickPathExtent);
658 server_info->debug=(GetLogEventMask() & CacheEvent) != 0 ? MagickTrue :
659 MagickFalse;
660 }
661 hostname=DestroyString(hostname);
662 return(server_info);
663}
664␌
665/*
666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667% %
668% %
669% %
670+ C o m p u t e H M A C S H A 2 5 6 M a c %
671% %
672% %
673% %
674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675%
676% ComputeHMACSHA256Mac() opens a pixel cache on a remote server.
677%
678% The format of the ComputeHMACSHA256Mac method is:
679%
680% int ComputeHMACSHA256Mac(const void *key,size_t key_length,
681% const void *message,size_t message_length,unsigned char *mac,
682% size_t mac_length)
683%
684% A description of each parameter follows:
685%
686% o key: the HMAC key.
687%
688% o key_length: HMAC key length in bytes.
689%
690% o message: the message.
691%
692% o message_length: the message length in bytes.
693%
694% o mac: the mac is computed and returned in this buffer.
695%
696% o mac_length: the mac length in bytes . mac must be at least this length.
697%
698*/
699
700static void InitializeSHA256Hash(SHA256Hash *sha256_hash)
701{
702 sha256_hash->count=0;
703 sha256_hash->state[0]=0x6a09e667;
704 sha256_hash->state[1]=0xbb67ae85;
705 sha256_hash->state[2]=0x3c6ef372;
706 sha256_hash->state[3]=0xa54ff53a;
707 sha256_hash->state[4]=0x510e527f;
708 sha256_hash->state[5]=0x9b05688c;
709 sha256_hash->state[6]=0x1f83d9ab;
710 sha256_hash->state[7]=0x5be0cd19;
711}
712
713static void TransformSHA256Hash(SHA256Hash *sha256_hash,
714 const unsigned char data[])
715{
716#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
717#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
718#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
719#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
720#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
721#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
722#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
723
724 static const uint32_t K[64] =
725 {
726 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
727 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
728 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
729 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
730 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
731 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
732 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
733 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
734 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
735 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
736 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
737 };
738
739 uint32_t
740 a,
741 b,
742 c,
743 d,
744 e,
745 f,
746 g,
747 h,
748 i,
749 j,
750 m[64];
751
752 j=0;
753 for (i=0; i < 16; i++, j+=4)
754 m[i]=((uint32_t) data[j] << 24) | ((uint32_t) data[j+1] << 16) |
755 ((uint32_t) data[j+2] << 8) | ((uint32_t) data[j+3]);
756 for ( ; i < 64; i++)
757 m[i]=SIG1(m[i-2])+m[i-7]+SIG0(m[i-15])+m[i-16];
758 a=sha256_hash->state[0];
759 b=sha256_hash->state[1];
760 c=sha256_hash->state[2];
761 d=sha256_hash->state[3];
762 e=sha256_hash->state[4];
763 f=sha256_hash->state[5];
764 g=sha256_hash->state[6];
765 h=sha256_hash->state[7];
766 for (i=0; i < 64; i++)
767 {
768 uint32_t
769 alpha = h+EP1(e)+CH(e,f,g)+K[i]+m[i],
770 beta = EP0(a)+MAJ(a,b,c);
771
772 h=g;
773 g=f;
774 f=e;
775 e=d+alpha;
776 d=c;
777 c=b;
778 b=a;
779 a=alpha+beta;
780 }
781 sha256_hash->state[0]+=a;
782 sha256_hash->state[1]+=b;
783 sha256_hash->state[2]+=c;
784 sha256_hash->state[3]+=d;
785 sha256_hash->state[4]+=e;
786 sha256_hash->state[5]+=f;
787 sha256_hash->state[6]+=g;
788 sha256_hash->state[7]+=h;
789}
790
791static void UpdateSHA256Hash(SHA256Hash *sha256_hash,const unsigned char *data,
792 const size_t data_length)
793{
794 size_t
795 i,
796 index = (size_t) ((sha256_hash->count/8) % 64);
797
798 sha256_hash->count+=(uint64_t) data_length*8;
799 for (i=0; i < data_length; i++)
800 {
801 sha256_hash->buffer[index++]=data[i];
802 if (index == 64)
803 {
804 TransformSHA256Hash(sha256_hash,sha256_hash->buffer);
805 index=0;
806 }
807 }
808}
809
810static void FinalizeSHA256Hash(SHA256Hash *sha256_hash,unsigned char hash[32])
811{
812 size_t
813 i,
814 index = (size_t) ((sha256_hash->count/8) % 64),
815 pad_length = (index < 56) ? (56-index) : (120-index);
816
817 uint64_t
818 bit_count = sha256_hash->count; /* message length before padding */
819
820 unsigned char
821 pad[64];
822
823 pad[0]=0x80;
824 (void) memset(pad+1,0,pad_length-1);
825 UpdateSHA256Hash(sha256_hash,pad,pad_length);
826 for (i=0; i < 8; i++)
827 pad[i]=(unsigned char) (bit_count >> ((7-i)*8));
828 UpdateSHA256Hash(sha256_hash, pad, 8);
829 for (i=0; i < 32; i++)
830 hash[i]=(unsigned char) (sha256_hash->state[i/4] >> ((3-(i % 4))*8));
831}
832
833static void ComputeSHA256Hash(const unsigned char *data,size_t length,
834 unsigned char hash[32])
835{
837 sha256_hash;
838
839 /*
840 Hash a buffer in one shot.
841 */
842 InitializeSHA256Hash(&sha256_hash);
843 UpdateSHA256Hash(&sha256_hash,data,length);
844 FinalizeSHA256Hash(&sha256_hash,hash);
845}
846
847static int ComputeHMACSHA256Mac(const void *key,size_t key_length,
848 const void *message,size_t message_length,unsigned char *mac,
849 size_t mac_length)
850{
852 sha256_hash;
853
854 size_t
855 i;
856
857 unsigned char
858 full_hash[32],
859 inner_hash[32],
860 inner_pad[64],
861 k_prime[64]={0},
862 outer_pad[64];
863
864 /*
865 If key is longer than 64 bytes, hash it down first.
866 */
867 if ((mac == (unsigned char *) NULL) || (mac_length == 0))
868 return(-1);
869 if (((key == (const void *) NULL) && (key_length != 0)) ||
870 ((message == (const void *) NULL) && (message_length != 0)))
871 return(-1);
872 if (key_length > 64)
873 ComputeSHA256Hash((unsigned char *) key,key_length,k_prime);
874 else
875 (void) memcpy(k_prime,key,key_length);
876 /*
877 Prepare inner pad and outer pad masks.
878 */
879 for (i=0; i < 64; i++)
880 {
881 inner_pad[i]=k_prime[i] ^ 0x36;
882 outer_pad[i]=k_prime[i] ^ 0x5c;
883 }
884 /*
885 Inner hash: SHA256(inner pad || message)
886 */
887 InitializeSHA256Hash(&sha256_hash);
888 UpdateSHA256Hash(&sha256_hash,inner_pad,64);
889 UpdateSHA256Hash(&sha256_hash,(unsigned char *) message,message_length);
890 FinalizeSHA256Hash(&sha256_hash,inner_hash);
891 /*
892 Outer hash: SHA256(outer pad || inner hash)
893 */
894 InitializeSHA256Hash(&sha256_hash);
895 UpdateSHA256Hash(&sha256_hash,outer_pad,64);
896 UpdateSHA256Hash(&sha256_hash,inner_hash,32);
897 FinalizeSHA256Hash(&sha256_hash,full_hash);
898 /*
899 Truncate or copy to destination buffer based on requested mac length.
900 */
901 (void) memcpy(mac,full_hash,(mac_length < 32) ? mac_length : 32);
902 return(0);
903}
904␌
905/*
906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907% %
908% %
909% %
910+ D e s t r o y D i s t r i b u t e C a c h e I n f o %
911% %
912% %
913% %
914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915%
916% DestroyDistributeCacheInfo() deallocates memory associated with an
917% DistributeCacheInfo structure.
918%
919% The format of the DestroyDistributeCacheInfo method is:
920%
921% DistributeCacheInfo *DestroyDistributeCacheInfo(
922% DistributeCacheInfo *server_info)
923%
924% A description of each parameter follows:
925%
926% o server_info: the distributed cache info.
927%
928*/
929MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
930 DistributeCacheInfo *server_info)
931{
932 assert(server_info != (DistributeCacheInfo *) NULL);
933 assert(server_info->signature == MagickCoreSignature);
934#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
935 if (server_info->file >= 0)
936 CLOSE_SOCKET(server_info->file);
937#endif
938 server_info->signature=(~MagickCoreSignature);
939 server_info=(DistributeCacheInfo *) RelinquishMagickMemory(server_info);
940 return(server_info);
941}
942␌
943/*
944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945% %
946% %
947% %
948+ D i s t r i b u t e P i x e l C a c h e S e r v e r %
949% %
950% %
951% %
952%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
953%
954% DistributePixelCacheServer() waits on the specified port for commands to
955% create, read, update, or destroy a pixel cache.
956%
957% The format of the DistributePixelCacheServer() method is:
958%
959% void DistributePixelCacheServer(const int port)
960%
961% A description of each parameter follows:
962%
963% o port: connect the distributed pixel cache at this port.
964%
965% o exception: return any errors or warnings in this structure.
966%
967*/
968
969#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
970static inline MagickOffsetType dpc_send(SOCKET_TYPE magick_unused(file),
971 const MagickSizeType magick_unused(length),
972 const void *magick_restrict magick_unused(message))
973{
974 magick_unreferenced(file);
975 magick_unreferenced(length);
976 magick_unreferenced(message);
977 return(-1);
978}
979#else
980static inline MagickOffsetType dpc_send(SOCKET_TYPE file,
981 const MagickSizeType length,const void *magick_restrict message)
982{
983 MagickOffsetType
984 i;
985
986 ssize_t
987 count;
988
989 /*
990 Ensure a complete message is sent.
991 */
992 count=0;
993 for (i=0; i < (MagickOffsetType) length; i+=count)
994 {
995 count=(ssize_t) send(file,(const char *) message+i,(LENGTH_TYPE) MagickMin(
996 length-(MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent),
997 MSG_NOSIGNAL);
998 if (count <= 0)
999 {
1000 count=0;
1001 if (errno != EINTR)
1002 break;
1003 }
1004 }
1005 return(i);
1006}
1007#endif
1008
1009static const char *GetDPCSharedSecret(void)
1010{
1011#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
1012 /*
1013 The server loads the secret at start-up; a client loads it on first use.
1014 */
1015 if (dpc_shared_secret == (char *) NULL)
1016 {
1017 if (dpc_semaphore == (SemaphoreInfo *) NULL)
1018 ActivateSemaphoreInfo(&dpc_semaphore);
1019 LockSemaphoreInfo(dpc_semaphore);
1020 if (dpc_shared_secret == (char *) NULL)
1021 dpc_shared_secret=GetPolicyValue("cache:shared-secret");
1022 UnlockSemaphoreInfo(dpc_semaphore);
1023 }
1024 return(dpc_shared_secret);
1025#else
1026 return((const char *) NULL);
1027#endif
1028}
1029
1030static int DeriveDPCKey(const uint64_t session_key,const char label,
1031 unsigned char key[DPCMacLength])
1032{
1033 const char
1034 *shared_secret;
1035
1036 unsigned char
1037 info[sizeof(session_key)+1];
1038
1039 /*
1040 key = HMAC-SHA-256(shared secret, session key || label). The session key
1041 travels in the clear, so the shared secret is what keeps this private.
1042 */
1043 shared_secret=GetDPCSharedSecret();
1044 if (shared_secret == (const char *) NULL)
1045 return(-1);
1046 (void) memcpy(info,&session_key,sizeof(session_key));
1047 info[sizeof(session_key)]=(unsigned char) label;
1048 return(ComputeHMACSHA256Mac(shared_secret,strlen(shared_secret),info,
1049 sizeof(info),key,DPCMacLength));
1050}
1051
1052static inline uint64_t LoadDPCWord(const unsigned char *p)
1053{
1054 return(((uint64_t) p[0]) | ((uint64_t) p[1] << 8) |
1055 ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) |
1056 ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) |
1057 ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56));
1058}
1059
1060static void SplitMix64Keystream(const unsigned char *stream_key,
1061 const unsigned char *mac,const MagickSizeType offset,unsigned char *data,
1062 const size_t length)
1063{
1064 size_t
1065 i;
1066
1067 uint64_t
1068 block,
1069 seed,
1070 z;
1071
1072 /*
1073 XOR data with the SplitMix64 keystream starting at byte 'offset'.
1074 */
1075 seed=LoadDPCWord(stream_key) ^ LoadDPCWord(mac);
1076 block=(uint64_t) (offset >> 3);
1077 z=0;
1078 for (i=0; i < length; i++)
1079 {
1080 size_t
1081 j = (size_t) ((offset+i) & 7);
1082
1083 if ((i == 0) || (j == 0))
1084 {
1085 block=(uint64_t) ((offset+i) >> 3);
1086 z=seed+(block+1)*0x9e3779b97f4a7c15ULL;
1087 z=(z ^ (z >> 30))*0xbf58476d1ce4e5b9ULL;
1088 z=(z ^ (z >> 27))*0x94d049bb133111ebULL;
1089 z^=z >> 31;
1090 }
1091 data[i]^=(unsigned char) (z >> (j*8));
1092 }
1093}
1094
1095static inline void WipeDPCKey(unsigned char *key,const size_t length)
1096{
1097 volatile unsigned char
1098 *p = key;
1099
1100 size_t
1101 i;
1102
1103 for (i=0; i < length; i++)
1104 p[i]=0;
1105}
1106
1107static inline int mac_compare(const unsigned char *a,const unsigned char *b,
1108 const size_t length)
1109{
1110 size_t
1111 i;
1112
1113 volatile unsigned char
1114 difference = 0;
1115
1116 /*
1117 Compare in time independent of where (or whether) the buffers differ.
1118 */
1119 for (i=0; i < length; i++)
1120 difference|=(unsigned char) (a[i] ^ b[i]);
1121 return(difference == 0 ? 0 : -1);
1122}
1123
1124static inline MagickOffsetType dpc_send_mac(SOCKET_TYPE file,
1125 const MagickSizeType length,const void *magick_restrict message,
1126 uint64_t session_key)
1127{
1128 const unsigned char
1129 *p = (const unsigned char *) message;
1130
1131 MagickOffsetType
1132 status;
1133
1134 MagickSizeType
1135 offset;
1136
1137 unsigned char
1138 mac[DPCMacLength],
1139 mac_key[DPCMacLength],
1140 scratch[DPCScratchExtent],
1141 stream_key[DPCMacLength];
1142
1143 /*
1144 MAC the plaintext, then send MAC || (plaintext XOR keystream).
1145 */
1146 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
1147 return(-1);
1148 if ((DeriveDPCKey(session_key,'m',mac_key) != 0) ||
1149 (DeriveDPCKey(session_key,'e',stream_key) != 0) ||
1150 (ComputeHMACSHA256Mac(mac_key,sizeof(mac_key),message,(size_t) length, mac,sizeof(mac)) != 0))
1151 {
1152 WipeDPCKey(mac_key,sizeof(mac_key));
1153 WipeDPCKey(stream_key,sizeof(stream_key));
1154 return(-1); /* key derivation or HMAC computation failed */
1155 }
1156 WipeDPCKey(mac_key,sizeof(mac_key));
1157 status=dpc_send(file,sizeof(mac),mac);
1158 if (status != (MagickOffsetType) sizeof(mac))
1159 {
1160 WipeDPCKey(stream_key,sizeof(stream_key));
1161 return(-1);
1162 }
1163 /*
1164 Encrypt into a scratch buffer: the caller's buffer may be the live cache.
1165 */
1166 for (offset=0; offset < length; )
1167 {
1168 size_t
1169 chunk = (size_t) MagickMin(length-offset,(MagickSizeType)
1170 sizeof(scratch));
1171
1172 (void) memcpy(scratch,p+offset,chunk);
1173 SplitMix64Keystream(stream_key,mac,offset,scratch,chunk);
1174 status=dpc_send(file,(MagickSizeType) chunk,scratch);
1175 if (status != (MagickOffsetType) chunk)
1176 break;
1177 offset+=(MagickSizeType) chunk;
1178 }
1179 WipeDPCKey(stream_key,sizeof(stream_key));
1180 WipeDPCKey(scratch,sizeof(scratch));
1181 if (offset != length)
1182 return(-1);
1183 return((MagickOffsetType) length);
1184}
1185
1186static inline MagickOffsetType dpc_receive_mac(SOCKET_TYPE file,
1187 const MagickSizeType length,unsigned char *magick_restrict message,
1188 uint64_t session_key)
1189{
1190 MagickOffsetType
1191 count;
1192
1193 unsigned char
1194 expected_mac[DPCMacLength],
1195 mac_key[DPCMacLength],
1196 received_mac[DPCMacLength],
1197 stream_key[DPCMacLength];
1198
1199 int
1200 status;
1201
1202 /*
1203 Read MAC || ciphertext, decrypt in place, then verify the MAC.
1204 */
1205 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
1206 return(-1);
1207 count=dpc_receive(file,sizeof(received_mac),received_mac);
1208 if (count != (MagickOffsetType) sizeof(received_mac))
1209 return(-1);
1210 count=dpc_receive(file,length,message);
1211 if (count != (MagickOffsetType) length)
1212 return(count);
1213 status=DeriveDPCKey(session_key,'e',stream_key);
1214 if (status == 0)
1215 {
1216 SplitMix64Keystream(stream_key,received_mac,0,message,(size_t) length);
1217 status=DeriveDPCKey(session_key,'m',mac_key);
1218 }
1219 if (status == 0)
1220 status=ComputeHMACSHA256Mac(mac_key,sizeof(mac_key),message,
1221 (size_t) length,expected_mac,sizeof(expected_mac));
1222 WipeDPCKey(mac_key,sizeof(mac_key));
1223 WipeDPCKey(stream_key,sizeof(stream_key));
1224 if (status != 0)
1225 return(-1); /* key derivation or HMAC computation failed */
1226 if (mac_compare(received_mac,expected_mac,sizeof(expected_mac)) != 0)
1227 return(-1); /* HMAC verification failed */
1228 return(count);
1229}
1230
1231#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
1232MagickExport void DistributePixelCacheServer(const int magick_unused(port),
1233 ExceptionInfo *magick_unused(exception))
1234{
1235 magick_unreferenced(port);
1236 magick_unreferenced(exception);
1237 ThrowFatalException(MissingDelegateError,"DelegateLibrarySupportNotBuiltIn");
1238}
1239#else
1240static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
1241 const uint64_t session_key)
1242{
1243 MagickAddressType
1244 key = (MagickAddressType) session_key;
1245
1246 /*
1247 Destroy distributed pixel cache.
1248 */
1249 return(DeleteNodeFromSplayTree(registry,(const void *) key));
1250}
1251
1252static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
1253 SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
1254{
1255 Image
1256 *image;
1257
1258 MagickAddressType
1259 key = (MagickAddressType) session_key;
1260
1261 MagickBooleanType
1262 status;
1263
1264 MagickOffsetType
1265 count;
1266
1267 MagickSizeType
1268 length;
1269
1270 unsigned char
1271 message[MagickPathExtent],
1272 *p;
1273
1274 /*
1275 Open distributed pixel cache.
1276 */
1277 image=AcquireImage((ImageInfo *) NULL,exception);
1278 if (image == (Image *) NULL)
1279 return(MagickFalse);
1280 length=sizeof(image->storage_class)+sizeof(image->colorspace)+
1281 sizeof(image->alpha_trait)+sizeof(image->channels)+sizeof(image->columns)+
1282 sizeof(image->rows)+sizeof(image->number_channels)+MaxPixelChannels*
1283 sizeof(*image->channel_map)+sizeof(image->metacontent_extent);
1284 count=dpc_receive(file,length,message);
1285 if (count != (MagickOffsetType) length)
1286 {
1287 image=DestroyImage(image);
1288 return(MagickFalse);
1289 }
1290 /*
1291 Deserialize the image attributes.
1292 */
1293 p=message;
1294 (void) memcpy(&image->storage_class,p,sizeof(image->storage_class));
1295 p+=(ptrdiff_t) sizeof(image->storage_class);
1296 (void) memcpy(&image->colorspace,p,sizeof(image->colorspace));
1297 p+=(ptrdiff_t) sizeof(image->colorspace);
1298 (void) memcpy(&image->alpha_trait,p,sizeof(image->alpha_trait));
1299 p+=(ptrdiff_t) sizeof(image->alpha_trait);
1300 (void) memcpy(&image->channels,p,sizeof(image->channels));
1301 p+=(ptrdiff_t) sizeof(image->channels);
1302 (void) memcpy(&image->columns,p,sizeof(image->columns));
1303 p+=(ptrdiff_t) sizeof(image->columns);
1304 (void) memcpy(&image->rows,p,sizeof(image->rows));
1305 p+=(ptrdiff_t) sizeof(image->rows);
1306 (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
1307 p+=(ptrdiff_t) sizeof(image->number_channels);
1308 (void) memcpy(image->channel_map,p,MaxPixelChannels*
1309 sizeof(*image->channel_map));
1310 p+=(ptrdiff_t) MaxPixelChannels*sizeof(*image->channel_map);
1311 (void) memcpy(&image->metacontent_extent,p,sizeof(image->metacontent_extent));
1312 p+=(ptrdiff_t) sizeof(image->metacontent_extent);
1313 if (SyncImagePixelCache(image,exception) == MagickFalse)
1314 {
1315 image=DestroyImage(image);
1316 return(MagickFalse);
1317 }
1318 status=AddValueToSplayTree(registry,(const void *) key,image);
1319 if (status == MagickFalse)
1320 {
1321 image=DestroyImage(image);
1322 return(MagickFalse);
1323 }
1324 return(status);
1325}
1326
1327static inline MagickBooleanType ValidateDistributedPixelCache(
1328 const RectangleInfo *region,const size_t per_pixel,
1329 const MagickSizeType length)
1330{
1331 size_t
1332 extent = 0,
1333 pixels = 0;
1334
1335 if (HeapOverflowSanityCheckGetSize(region->width,region->height,&pixels) != MagickFalse)
1336 return(MagickFalse);
1337 if (HeapOverflowSanityCheckGetSize(pixels,per_pixel,&extent) != MagickFalse)
1338 return(MagickFalse);
1339 if (length > (MagickSizeType) extent)
1340 return(MagickFalse);
1341 return(MagickTrue);
1342}
1343
1344static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
1345 SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
1346{
1347 const Quantum
1348 *p;
1349
1350 const unsigned char
1351 *metacontent;
1352
1353 Image
1354 *image;
1355
1356 MagickAddressType
1357 key = (MagickAddressType) session_key;
1358
1359 MagickOffsetType
1360 count;
1361
1362 MagickSizeType
1363 length;
1364
1365 RectangleInfo
1366 region;
1367
1368 size_t
1369 per_pixel;
1370
1371 unsigned char
1372 message[MagickPathExtent],
1373 *q;
1374
1375 /*
1376 Read distributed pixel cache metacontent.
1377 */
1378 image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1379 if (image == (Image *) NULL)
1380 return(MagickFalse);
1381 length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1382 sizeof(region.y)+sizeof(length);
1383 count=dpc_receive(file,length,message);
1384 if (count != (MagickOffsetType) length)
1385 return(MagickFalse);
1386 q=message;
1387 (void) memcpy(&region.width,q,sizeof(region.width));
1388 q+=(ptrdiff_t) sizeof(region.width);
1389 (void) memcpy(&region.height,q,sizeof(region.height));
1390 q+=(ptrdiff_t) sizeof(region.height);
1391 (void) memcpy(&region.x,q,sizeof(region.x));
1392 q+=(ptrdiff_t) sizeof(region.x);
1393 (void) memcpy(&region.y,q,sizeof(region.y));
1394 q+=(ptrdiff_t) sizeof(region.y);
1395 (void) memcpy(&length,q,sizeof(length));
1396 q+=(ptrdiff_t) sizeof(length);
1397 per_pixel=image->number_meta_channels*sizeof(Quantum);
1398 if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1399 return(MagickFalse);
1400 p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
1401 exception);
1402 if (p == (const Quantum *) NULL)
1403 return(MagickFalse);
1404 metacontent=(const unsigned char *) GetVirtualMetacontent(image);
1405 count=dpc_send_mac(file,length,metacontent,session_key);
1406 if (count != (MagickOffsetType) length)
1407 return(MagickFalse);
1408 return(MagickTrue);
1409}
1410
1411static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
1412 SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
1413{
1414 const Quantum
1415 *p;
1416
1417 Image
1418 *image;
1419
1420 MagickAddressType
1421 key = (MagickAddressType) session_key;
1422
1423 MagickOffsetType
1424 count;
1425
1426 MagickSizeType
1427 length;
1428
1429 RectangleInfo
1430 region;
1431
1432 size_t
1433 per_pixel;
1434
1435 unsigned char
1436 message[MagickPathExtent],
1437 *q;
1438
1439 /*
1440 Read distributed pixel cache pixels.
1441 */
1442 image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1443 if (image == (Image *) NULL)
1444 return(MagickFalse);
1445 length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1446 sizeof(region.y)+sizeof(length);
1447 count=dpc_receive(file,length,message);
1448 if (count != (MagickOffsetType) length)
1449 return(MagickFalse);
1450 q=message;
1451 (void) memcpy(&region.width,q,sizeof(region.width));
1452 q+=(ptrdiff_t) sizeof(region.width);
1453 (void) memcpy(&region.height,q,sizeof(region.height));
1454 q+=(ptrdiff_t) sizeof(region.height);
1455 (void) memcpy(&region.x,q,sizeof(region.x));
1456 q+=(ptrdiff_t) sizeof(region.x);
1457 (void) memcpy(&region.y,q,sizeof(region.y));
1458 q+=(ptrdiff_t) sizeof(region.y);
1459 (void) memcpy(&length,q,sizeof(length));
1460 per_pixel=image->number_channels*sizeof(Quantum);
1461 if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1462 return(MagickFalse);
1463 q+=(ptrdiff_t) sizeof(length);
1464 p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
1465 exception);
1466 if (p == (const Quantum *) NULL)
1467 return(MagickFalse);
1468 count=dpc_send_mac(file,length,p,session_key);
1469 if (count != (MagickOffsetType) length)
1470 return(MagickFalse);
1471 return(MagickTrue);
1472}
1473
1474static void *RelinquishImageRegistry(void *image)
1475{
1476 return((void *) DestroyImageList((Image *) image));
1477}
1478
1479static MagickBooleanType WriteDistributeCacheMetacontent(
1480 SplayTreeInfo *registry,SOCKET_TYPE file,const uint64_t session_key,
1481 ExceptionInfo *exception)
1482{
1483 Image
1484 *image;
1485
1486 MagickAddressType
1487 key = (MagickAddressType) session_key;
1488
1489 MagickOffsetType
1490 count;
1491
1492 MagickSizeType
1493 length;
1494
1495 Quantum
1496 *q;
1497
1498 RectangleInfo
1499 region;
1500
1501 size_t
1502 per_pixel;
1503
1504 unsigned char
1505 message[MagickPathExtent],
1506 *metacontent,
1507 *p;
1508
1509 /*
1510 Write distributed pixel cache metacontent.
1511 */
1512 image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1513 if (image == (Image *) NULL)
1514 return(MagickFalse);
1515 length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1516 sizeof(region.y)+sizeof(length);
1517 count=dpc_receive(file,length,message);
1518 if (count != (MagickOffsetType) length)
1519 return(MagickFalse);
1520 p=message;
1521 (void) memcpy(&region.width,p,sizeof(region.width));
1522 p+=(ptrdiff_t) sizeof(region.width);
1523 (void) memcpy(&region.height,p,sizeof(region.height));
1524 p+=(ptrdiff_t) sizeof(region.height);
1525 (void) memcpy(&region.x,p,sizeof(region.x));
1526 p+=(ptrdiff_t) sizeof(region.x);
1527 (void) memcpy(&region.y,p,sizeof(region.y));
1528 p+=(ptrdiff_t) sizeof(region.y);
1529 (void) memcpy(&length,p,sizeof(length));
1530 per_pixel=image->number_meta_channels*sizeof(Quantum);
1531 if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1532 return(MagickFalse);
1533 p+=(ptrdiff_t) sizeof(length);
1534 q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
1535 exception);
1536 if (q == (Quantum *) NULL)
1537 return(MagickFalse);
1538 metacontent=(unsigned char *) GetAuthenticMetacontent(image);
1539 count=dpc_receive_mac(file,length,metacontent,session_key);
1540 if (count != (MagickOffsetType) length)
1541 return(MagickFalse);
1542 return(SyncAuthenticPixels(image,exception));
1543}
1544
1545static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
1546 SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
1547{
1548 Image
1549 *image;
1550
1551 MagickAddressType
1552 key = (MagickAddressType) session_key;
1553
1554 MagickOffsetType
1555 count;
1556
1557 MagickSizeType
1558 length;
1559
1560 Quantum
1561 *q;
1562
1563 RectangleInfo
1564 region;
1565
1566 size_t
1567 per_pixel;
1568
1569 unsigned char
1570 message[MagickPathExtent],
1571 *p;
1572
1573 /*
1574 Write distributed pixel cache pixels.
1575 */
1576 image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1577 if (image == (Image *) NULL)
1578 return(MagickFalse);
1579 length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1580 sizeof(region.y)+sizeof(length);
1581 count=dpc_receive(file,length,message);
1582 if (count != (MagickOffsetType) length)
1583 return(MagickFalse);
1584 p=message;
1585 (void) memcpy(&region.width,p,sizeof(region.width));
1586 p+=(ptrdiff_t) sizeof(region.width);
1587 (void) memcpy(&region.height,p,sizeof(region.height));
1588 p+=(ptrdiff_t) sizeof(region.height);
1589 (void) memcpy(&region.x,p,sizeof(region.x));
1590 p+=(ptrdiff_t) sizeof(region.x);
1591 (void) memcpy(&region.y,p,sizeof(region.y));
1592 p+=(ptrdiff_t) sizeof(region.y);
1593 (void) memcpy(&length,p,sizeof(length));
1594 per_pixel=image->number_channels*sizeof(Quantum);
1595 if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1596 return(MagickFalse);
1597 p+=(ptrdiff_t) sizeof(length);
1598 q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
1599 exception);
1600 if (q == (Quantum *) NULL)
1601 return(MagickFalse);
1602 count=dpc_receive_mac(file,length,(unsigned char *) q,session_key);
1603 if (count != (MagickOffsetType) length)
1604 return(MagickFalse);
1605 return(SyncAuthenticPixels(image,exception));
1606}
1607
1608static void LockDPCSemaphore(void)
1609{
1610 if (dpc_semaphore == (SemaphoreInfo *) NULL)
1611 ActivateSemaphoreInfo(&dpc_semaphore);
1612 LockSemaphoreInfo(dpc_semaphore);
1613}
1614
1615static void AuthenticateDPCClient(void)
1616{
1617 LockDPCSemaphore();
1618 if (dpc_unauthenticated_clients != 0)
1619 dpc_unauthenticated_clients--;
1620 UnlockSemaphoreInfo(dpc_semaphore);
1621}
1622
1623static void RelinquishDPCClient(const MagickBooleanType unauthenticated)
1624{
1625 LockDPCSemaphore();
1626 if ((unauthenticated != MagickFalse) && (dpc_unauthenticated_clients != 0))
1627 dpc_unauthenticated_clients--;
1628 if (dpc_clients != 0)
1629 dpc_clients--;
1630 UnlockSemaphoreInfo(dpc_semaphore);
1631}
1632
1633static HANDLER_RETURN_TYPE DistributePixelCacheConnectionHandler(
1634 void *socket_arg)
1635{
1636 ExceptionInfo
1637 *exception;
1638
1639 MagickBooleanType
1640 authenticated = MagickFalse,
1641 status = MagickFalse;
1642
1643 MagickOffsetType
1644 count;
1645
1646 RandomInfo
1647 *random_info;
1648
1649 SOCKET_TYPE
1650 client_socket,
1651 *client_socket_ptr = (SOCKET_TYPE *) socket_arg;
1652
1653 SplayTreeInfo
1654 *registry;
1655
1656 StringInfo
1657 *entropy;
1658
1659 uint64_t
1660 key,
1661 session_key;
1662
1663 unsigned char
1664 command,
1665 nonce[DPCSessionKeyLength];
1666
1667 client_socket=(*client_socket_ptr);
1668 client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(client_socket_ptr);
1669 /*
1670 Generate random nonce.
1671 */
1672 random_info=AcquireRandomInfo();
1673 entropy=GetRandomKey(random_info,sizeof(nonce));
1674 (void) memcpy(nonce,GetStringInfoDatum(entropy),sizeof(nonce));
1675 entropy=DestroyStringInfo(entropy);
1676 random_info=DestroyRandomInfo(random_info);
1677 /*
1678 Derive session key.
1679 */
1680 session_key=GenerateSessionKey(dpc_shared_secret,nonce,sizeof(nonce));
1681 /*
1682 Send nonce to client.
1683 */
1684 count=dpc_send(client_socket,sizeof(nonce),nonce);
1685 if (count != (MagickOffsetType) sizeof(nonce))
1686 {
1687 CLOSE_SOCKET(client_socket);
1688 RelinquishDPCClient(MagickTrue);
1689 return(HANDLER_RETURN_VALUE);
1690 }
1691 /*
1692 Receive client's keyed hash.
1693 */
1694 count=dpc_receive_deadline(client_socket,sizeof(key),(unsigned char *) &key,
1695 (double) time((time_t *) NULL)+5.0);
1696 if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
1697 {
1698 CLOSE_SOCKET(client_socket);
1699 RelinquishDPCClient(MagickTrue);
1700 return(HANDLER_RETURN_VALUE);
1701 }
1702 AuthenticateDPCClient();
1703 authenticated=MagickTrue;
1704 exception=AcquireExceptionInfo();
1705 registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
1706 (void *(*)(void *)) NULL,RelinquishImageRegistry);
1707 /*
1708 Command loop.
1709 */
1710 for (status=MagickFalse; ; )
1711 {
1712 /*
1713 Each command must echo the authenticated session key.
1714 */
1715 count=dpc_receive_deadline(client_socket,1,(unsigned char *) &command,
1716 (double) time((time_t *) NULL)+5.0);
1717 if (count <= 0)
1718 break;
1719 count=dpc_receive_deadline(client_socket,sizeof(key),(unsigned char *) &key,
1720 (double) time((time_t *) NULL)+5.0);
1721 if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
1722 break;
1723 switch (command)
1724 {
1725 case 'o':
1726 {
1727 status=OpenDistributeCache(registry,client_socket,session_key,
1728 exception);
1729 dpc_send(client_socket,sizeof(status),&status);
1730 break;
1731 }
1732 case 'r':
1733 {
1734 status=ReadDistributeCachePixels(registry,client_socket,session_key,
1735 exception);
1736 break;
1737 }
1738 case 'R':
1739 {
1740 status=ReadDistributeCacheMetacontent(registry,client_socket,
1741 session_key,exception);
1742 break;
1743 }
1744 case 'w':
1745 {
1746 status=WriteDistributeCachePixels(registry,client_socket,session_key,
1747 exception);
1748 break;
1749 }
1750 case 'W':
1751 {
1752 status=WriteDistributeCacheMetacontent(registry,client_socket,
1753 session_key,exception);
1754 break;
1755 }
1756 case 'd':
1757 {
1758 status=DestroyDistributeCache(registry,session_key);
1759 break;
1760 }
1761 default:
1762 break;
1763 }
1764 if ((status == MagickFalse) || (command == 'd'))
1765 break;
1766 }
1767 count=dpc_send(client_socket,sizeof(status),&status);
1768 CLOSE_SOCKET(client_socket);
1769 exception=DestroyExceptionInfo(exception);
1770 registry=DestroySplayTree(registry);
1771 RelinquishDPCClient(authenticated == MagickFalse ? MagickTrue :
1772 MagickFalse);
1773 return(HANDLER_RETURN_VALUE);
1774}
1775
1776static size_t GetDPCPolicyLimit(const char *name,const size_t default_limit)
1777{
1778 char
1779 policy_name[MagickPathExtent],
1780 *policy_value;
1781
1782 size_t
1783 limit;
1784
1785 limit=default_limit;
1786 (void) FormatLocaleString(policy_name,MagickPathExtent,"cache:%s",name);
1787 policy_value=GetPolicyValue(policy_name);
1788 if (policy_value != (char *) NULL)
1789 {
1790 char
1791 *q;
1792
1793 unsigned long
1794 policy_limit;
1795
1796 errno=0;
1797 policy_limit=strtoul(policy_value,&q,10);
1798 if ((errno == 0) && (q != policy_value) && (policy_limit != 0))
1799 limit=(size_t) policy_limit;
1800 policy_value=DestroyString(policy_value);
1801 }
1802 return(limit);
1803}
1804
1805static size_t GetMaxDPCClients(void)
1806{
1807 return(GetDPCPolicyLimit("max-dpc-clients",DPCMaxClientWorkers));
1808}
1809
1810static size_t GetMaxDPCUnauthenticatedClients(void)
1811{
1812 size_t
1813 client_workers,
1814 unauthenticated_workers;
1815
1816 client_workers=GetMaxDPCClients();
1817 unauthenticated_workers=GetDPCPolicyLimit("max-dpc-unauthenticated-clients",
1818 DPCMaxUnauthenticatedClientWorkers);
1819 return(MagickMin(unauthenticated_workers,client_workers));
1820}
1821
1822static MagickBooleanType AcquireDPCClientWorker(void)
1823{
1824 MagickBooleanType
1825 status;
1826
1827 size_t
1828 max_dpc_clients,
1829 unauthenticated_worker_limit;
1830
1831 status=MagickFalse;
1832 max_dpc_clients=GetMaxDPCClients();
1833 unauthenticated_worker_limit=GetMaxDPCUnauthenticatedClients();
1834 LockDPCSemaphore();
1835 if ((dpc_clients < max_dpc_clients) &&
1836 (dpc_unauthenticated_clients < unauthenticated_worker_limit))
1837 {
1838 dpc_clients++;
1839 dpc_unauthenticated_clients++;
1840 status=MagickTrue;
1841 }
1842 UnlockSemaphoreInfo(dpc_semaphore);
1843 return(status);
1844}
1845
1846MagickExport void DistributePixelCacheServer(const int port,
1847 ExceptionInfo *exception)
1848{
1849 char
1850 service[MagickPathExtent];
1851
1852 int
1853 status;
1854
1855#if defined(MAGICKCORE_THREAD_SUPPORT)
1856 pthread_attr_t
1857 attributes;
1858
1859 pthread_t
1860 thread_id;
1861#elif defined(_MSC_VER)
1862 DWORD
1863 threadID;
1864#else
1865 Not implemented!
1866#endif
1867
1868 SOCKET_TYPE
1869 server_socket;
1870
1871 struct addrinfo
1872 *p;
1873
1874 struct addrinfo
1875 hint,
1876 *result;
1877
1878 struct sockaddr_in
1879 address;
1880
1881 /*
1882 Launch distributed pixel cache server.
1883 */
1884 assert(exception != (ExceptionInfo *) NULL);
1885 assert(exception->signature == MagickCoreSignature);
1886 magick_unreferenced(exception);
1887 dpc_shared_secret=GetPolicyValue("cache:shared-secret");
1888 if (dpc_shared_secret == (char *) NULL)
1889 ThrowFatalException(CacheFatalError,"no shared secret");
1890#if defined(MAGICKCORE_HAVE_WINSOCK2)
1891 InitializeWinsock2(MagickFalse);
1892#endif
1893 (void) memset(&hint,0,sizeof(hint));
1894 hint.ai_family=AF_INET;
1895 hint.ai_socktype=SOCK_STREAM;
1896 hint.ai_flags=AI_PASSIVE;
1897 FormatLocaleString(service,MagickPathExtent,"%d",port);
1898 status=getaddrinfo(NULL,service,&hint,&result);
1899 if (status != 0)
1900 ThrowFatalException(CacheFatalError,"UnableToListen");
1901 server_socket=(SOCKET_TYPE) 0;
1902 for (p=result; p != NULL; p=p->ai_next)
1903 {
1904 int
1905 one = 1;
1906
1907 server_socket=(SOCKET_TYPE) socket(p->ai_family,p->ai_socktype,
1908 p->ai_protocol);
1909 if (server_socket == -1)
1910 continue;
1911 status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,(char *) &one,
1912 (socklen_t) sizeof(one));
1913 if (status == -1)
1914 {
1915 CLOSE_SOCKET(server_socket);
1916 continue;
1917 }
1918 SetSocketReceiveTimeout(server_socket,5.0);
1919 status=bind(server_socket,p->ai_addr,(socklen_t) p->ai_addrlen);
1920 if (status == -1)
1921 {
1922 CLOSE_SOCKET(server_socket);
1923 continue;
1924 }
1925 break;
1926 }
1927 if (p == (struct addrinfo *) NULL)
1928 ThrowFatalException(CacheFatalError,"UnableToBind");
1929 freeaddrinfo(result);
1930 status=listen(server_socket,DPCPendingConnections);
1931 if (status != 0)
1932 ThrowFatalException(CacheFatalError,"UnableToListen");
1933#if defined(MAGICKCORE_THREAD_SUPPORT)
1934 pthread_attr_init(&attributes);
1935 pthread_attr_setdetachstate(&attributes,PTHREAD_CREATE_DETACHED);
1936#endif
1937 for ( ; ; )
1938 {
1939 SOCKET_TYPE
1940 *client_socket_ptr;
1941
1942 socklen_t
1943 length = (socklen_t) sizeof(address);
1944
1945 client_socket_ptr=(SOCKET_TYPE *) AcquireMagickMemory(sizeof(SOCKET_TYPE));
1946 if (client_socket_ptr == NULL)
1947 continue; /* skip connection */
1948 *client_socket_ptr=(SOCKET_TYPE) accept(server_socket,(struct sockaddr *)
1949 &address,&length);
1950 if (*client_socket_ptr == -1)
1951 {
1952 client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1953 client_socket_ptr);
1954 continue;
1955 }
1956 if (AcquireDPCClientWorker() == MagickFalse)
1957 {
1958 CLOSE_SOCKET(*client_socket_ptr);
1959 client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1960 client_socket_ptr);
1961 continue;
1962 }
1963 SetSocketReceiveTimeout(*client_socket_ptr,5.0);
1964 SetSocketNoDelay(*client_socket_ptr);
1965 if (status == -1)
1966 {
1967 CLOSE_SOCKET(*client_socket_ptr);
1968 RelinquishDPCClient(MagickTrue);
1969 client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1970 client_socket_ptr);
1971 continue;
1972 }
1973#if defined(MAGICKCORE_THREAD_SUPPORT)
1974 status=pthread_create(&thread_id,&attributes,
1975 DistributePixelCacheConnectionHandler,(void *) client_socket_ptr);
1976 if (status != 0)
1977 {
1978 CLOSE_SOCKET(*client_socket_ptr);
1979 RelinquishDPCClient(MagickTrue);
1980 RelinquishMagickMemory(client_socket_ptr);
1981 continue;
1982 }
1983#elif defined(_MSC_VER)
1984 if (CreateThread(0,0,DistributePixelCacheConnectionHandler,(void*) client_socket_ptr,0,&threadID) == (HANDLE) NULL)
1985 {
1986 CLOSE_SOCKET(*client_socket_ptr);
1987 RelinquishDPCClient(MagickTrue);
1988 RelinquishMagickMemory(client_socket_ptr);
1989 continue;
1990 }
1991#else
1992 Not implemented!
1993#endif
1994 }
1995}
1996#endif
1997␌
1998/*
1999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2000% %
2001% %
2002% %
2003+ D i s t r i b u t e C a c h e T e r m i n u s %
2004% %
2005% %
2006% %
2007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008%
2009% DistributeCacheTerminus() destroys the Distributed Cache.
2010%
2011*/
2012MagickPrivate void DistributeCacheTerminus(void)
2013{
2014#ifdef MAGICKCORE_HAVE_WINSOCK2
2015 if (winsock_semaphore == (SemaphoreInfo *) NULL)
2016 ActivateSemaphoreInfo(&winsock_semaphore);
2017 LockSemaphoreInfo(winsock_semaphore);
2018 if (wsaData != (WSADATA *) NULL)
2019 {
2020 WSACleanup();
2021 wsaData=(WSADATA *) RelinquishMagickMemory((void *) wsaData);
2022 }
2023 UnlockSemaphoreInfo(winsock_semaphore);
2024 RelinquishSemaphoreInfo(&winsock_semaphore);
2025#endif
2026#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
2027 if (dpc_shared_secret != (char *) NULL)
2028 dpc_shared_secret=DestroyString(dpc_shared_secret);
2029 if (dpc_semaphore != (SemaphoreInfo *) NULL)
2030 RelinquishSemaphoreInfo(&dpc_semaphore);
2031#endif
2032}
2033
2034/*
2035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2036% %
2037% %
2038% %
2039+ G e t D i s t r i b u t e C a c h e F i l e %
2040% %
2041% %
2042% %
2043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2044%
2045% GetDistributeCacheFile() returns the file associated with this
2046% DistributeCacheInfo structure.
2047%
2048% The format of the GetDistributeCacheFile method is:
2049%
2050% int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
2051%
2052% A description of each parameter follows:
2053%
2054% o server_info: the distributed cache info.
2055%
2056*/
2057MagickPrivate int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
2058{
2059 assert(server_info != (DistributeCacheInfo *) NULL);
2060 assert(server_info->signature == MagickCoreSignature);
2061 return(server_info->file);
2062}
2063␌
2064/*
2065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2066% %
2067% %
2068% %
2069+ G e t D i s t r i b u t e C a c h e H o s t n a m e %
2070% %
2071% %
2072% %
2073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2074%
2075% GetDistributeCacheHostname() returns the hostname associated with this
2076% DistributeCacheInfo structure.
2077%
2078% The format of the GetDistributeCacheHostname method is:
2079%
2080% const char *GetDistributeCacheHostname(
2081% const DistributeCacheInfo *server_info)
2082%
2083% A description of each parameter follows:
2084%
2085% o server_info: the distributed cache info.
2086%
2087*/
2088MagickPrivate const char *GetDistributeCacheHostname(
2089 const DistributeCacheInfo *server_info)
2090{
2091 assert(server_info != (DistributeCacheInfo *) NULL);
2092 assert(server_info->signature == MagickCoreSignature);
2093 return(server_info->hostname);
2094}
2095␌
2096/*
2097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2098% %
2099% %
2100% %
2101+ G e t D i s t r i b u t e C a c h e P o r t %
2102% %
2103% %
2104% %
2105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106%
2107% GetDistributeCachePort() returns the port associated with this
2108% DistributeCacheInfo structure.
2109%
2110% The format of the GetDistributeCachePort method is:
2111%
2112% int GetDistributeCachePort(const DistributeCacheInfo *server_info)
2113%
2114% A description of each parameter follows:
2115%
2116% o server_info: the distributed cache info.
2117%
2118*/
2119MagickPrivate int GetDistributeCachePort(const DistributeCacheInfo *server_info)
2120{
2121 assert(server_info != (DistributeCacheInfo *) NULL);
2122 assert(server_info->signature == MagickCoreSignature);
2123 return(server_info->port);
2124}
2125␌
2126/*
2127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2128% %
2129% %
2130% %
2131+ O p e n D i s t r i b u t e P i x e l C a c h e %
2132% %
2133% %
2134% %
2135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2136%
2137% OpenDistributePixelCache() opens a pixel cache on a remote server.
2138%
2139% The format of the OpenDistributePixelCache method is:
2140%
2141% MagickBooleanType *OpenDistributePixelCache(
2142% DistributeCacheInfo *server_info,Image *image)
2143%
2144% A description of each parameter follows:
2145%
2146% o server_info: the distributed cache info.
2147%
2148% o image: the image.
2149%
2150*/
2151MagickPrivate MagickBooleanType OpenDistributePixelCache(
2152 DistributeCacheInfo *server_info,Image *image)
2153{
2154 MagickBooleanType
2155 status;
2156
2157 MagickOffsetType
2158 count;
2159
2160 unsigned char
2161 message[MagickPathExtent],
2162 *p;
2163
2164 /*
2165 Open distributed pixel cache.
2166 */
2167 assert(server_info != (DistributeCacheInfo *) NULL);
2168 assert(server_info->signature == MagickCoreSignature);
2169 assert(image != (Image *) NULL);
2170 assert(image->signature == MagickCoreSignature);
2171 p=message;
2172 *p++='o'; /* open */
2173 /*
2174 Serialize image attributes (see ValidatePixelCacheMorphology()).
2175 */
2176 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2177 p+=(ptrdiff_t) sizeof(server_info->session_key);
2178 (void) memcpy(p,&image->storage_class,sizeof(image->storage_class));
2179 p+=(ptrdiff_t) sizeof(image->storage_class);
2180 (void) memcpy(p,&image->colorspace,sizeof(image->colorspace));
2181 p+=(ptrdiff_t) sizeof(image->colorspace);
2182 (void) memcpy(p,&image->alpha_trait,sizeof(image->alpha_trait));
2183 p+=(ptrdiff_t) sizeof(image->alpha_trait);
2184 (void) memcpy(p,&image->channels,sizeof(image->channels));
2185 p+=(ptrdiff_t) sizeof(image->channels);
2186 (void) memcpy(p,&image->columns,sizeof(image->columns));
2187 p+=(ptrdiff_t) sizeof(image->columns);
2188 (void) memcpy(p,&image->rows,sizeof(image->rows));
2189 p+=(ptrdiff_t) sizeof(image->rows);
2190 (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
2191 p+=(ptrdiff_t) sizeof(image->number_channels);
2192 (void) memcpy(p,image->channel_map,MaxPixelChannels*
2193 sizeof(*image->channel_map));
2194 p+=(ptrdiff_t) MaxPixelChannels*sizeof(*image->channel_map);
2195 (void) memcpy(p,&image->metacontent_extent,sizeof(image->metacontent_extent));
2196 p+=(ptrdiff_t) sizeof(image->metacontent_extent);
2197 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2198 if (count != (MagickOffsetType) (p-message))
2199 return(MagickFalse);
2200 status=MagickFalse;
2201 count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
2202 if (count != (MagickOffsetType) sizeof(status))
2203 return(MagickFalse);
2204 return(status);
2205}
2206␌
2207/*
2208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2209% %
2210% %
2211% %
2212+ R e a d D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t %
2213% %
2214% %
2215% %
2216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2217%
2218% ReadDistributePixelCacheMetacontents() reads metacontent from the specified
2219% region of the distributed pixel cache.
2220%
2221% The format of the ReadDistributePixelCacheMetacontents method is:
2222%
2223% MagickOffsetType ReadDistributePixelCacheMetacontents(
2224% DistributeCacheInfo *server_info,const RectangleInfo *region,
2225% const MagickSizeType length,unsigned char *metacontent)
2226%
2227% A description of each parameter follows:
2228%
2229% o server_info: the distributed cache info.
2230%
2231% o image: the image.
2232%
2233% o region: read the metacontent from this region of the image.
2234%
2235% o length: the length in bytes of the metacontent.
2236%
2237% o metacontent: read these metacontent from the pixel cache.
2238%
2239*/
2240MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
2241 DistributeCacheInfo *server_info,const RectangleInfo *region,
2242 const MagickSizeType length,unsigned char *metacontent)
2243{
2244 MagickOffsetType
2245 count;
2246
2247 unsigned char
2248 message[MagickPathExtent],
2249 *p;
2250
2251 /*
2252 Read distributed pixel cache metacontent.
2253 */
2254 assert(server_info != (DistributeCacheInfo *) NULL);
2255 assert(server_info->signature == MagickCoreSignature);
2256 assert(region != (RectangleInfo *) NULL);
2257 assert(metacontent != (unsigned char *) NULL);
2258 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
2259 return(-1);
2260 p=message;
2261 *p++='R';
2262 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2263 p+=(ptrdiff_t) sizeof(server_info->session_key);
2264 (void) memcpy(p,&region->width,sizeof(region->width));
2265 p+=(ptrdiff_t) sizeof(region->width);
2266 (void) memcpy(p,&region->height,sizeof(region->height));
2267 p+=(ptrdiff_t) sizeof(region->height);
2268 (void) memcpy(p,&region->x,sizeof(region->x));
2269 p+=(ptrdiff_t) sizeof(region->x);
2270 (void) memcpy(p,&region->y,sizeof(region->y));
2271 p+=(ptrdiff_t) sizeof(region->y);
2272 (void) memcpy(p,&length,sizeof(length));
2273 p+=(ptrdiff_t) sizeof(length);
2274 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2275 if (count != (MagickOffsetType) (p-message))
2276 return(-1);
2277 return(dpc_receive_mac(server_info->file,length,metacontent,
2278 server_info->session_key));
2279}
2280␌
2281/*
2282%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283% %
2284% %
2285% %
2286+ R e a d D i s t r i b u t e P i x e l C a c h e P i x e l s %
2287% %
2288% %
2289% %
2290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2291%
2292% ReadDistributePixelCachePixels() reads pixels from the specified region of
2293% the distributed pixel cache.
2294%
2295% The format of the ReadDistributePixelCachePixels method is:
2296%
2297% MagickOffsetType ReadDistributePixelCachePixels(
2298% DistributeCacheInfo *server_info,const RectangleInfo *region,
2299% const MagickSizeType length,unsigned char *magick_restrict pixels)
2300%
2301% A description of each parameter follows:
2302%
2303% o server_info: the distributed cache info.
2304%
2305% o image: the image.
2306%
2307% o region: read the pixels from this region of the image.
2308%
2309% o length: the length in bytes of the pixels.
2310%
2311% o pixels: read these pixels from the pixel cache.
2312%
2313*/
2314MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
2315 DistributeCacheInfo *server_info,const RectangleInfo *region,
2316 const MagickSizeType length,unsigned char *magick_restrict pixels)
2317{
2318 MagickOffsetType
2319 count;
2320
2321 unsigned char
2322 message[MagickPathExtent],
2323 *p;
2324
2325 /*
2326 Read distributed pixel cache pixels.
2327 */
2328 assert(server_info != (DistributeCacheInfo *) NULL);
2329 assert(server_info->signature == MagickCoreSignature);
2330 assert(region != (RectangleInfo *) NULL);
2331 assert(pixels != (unsigned char *) NULL);
2332 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
2333 return(-1);
2334 p=message;
2335 *p++='r';
2336 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2337 p+=(ptrdiff_t) sizeof(server_info->session_key);
2338 (void) memcpy(p,&region->width,sizeof(region->width));
2339 p+=(ptrdiff_t) sizeof(region->width);
2340 (void) memcpy(p,&region->height,sizeof(region->height));
2341 p+=(ptrdiff_t) sizeof(region->height);
2342 (void) memcpy(p,&region->x,sizeof(region->x));
2343 p+=(ptrdiff_t) sizeof(region->x);
2344 (void) memcpy(p,&region->y,sizeof(region->y));
2345 p+=(ptrdiff_t) sizeof(region->y);
2346 (void) memcpy(p,&length,sizeof(length));
2347 p+=(ptrdiff_t) sizeof(length);
2348 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2349 if (count != (MagickOffsetType) (p-message))
2350 return(-1);
2351 return(dpc_receive_mac(server_info->file,length,pixels,
2352 server_info->session_key));
2353}
2354␌
2355/*
2356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2357% %
2358% %
2359% %
2360+ R e l i n q u i s h D i s t r i b u t e P i x e l C a c h e %
2361% %
2362% %
2363% %
2364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2365%
2366% RelinquishDistributePixelCache() frees resources acquired with
2367% OpenDistributePixelCache().
2368%
2369% The format of the RelinquishDistributePixelCache method is:
2370%
2371% MagickBooleanType RelinquishDistributePixelCache(
2372% DistributeCacheInfo *server_info)
2373%
2374% A description of each parameter follows:
2375%
2376% o server_info: the distributed cache info.
2377%
2378*/
2379MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
2380 DistributeCacheInfo *server_info)
2381{
2382 MagickBooleanType
2383 status;
2384
2385 MagickOffsetType
2386 count;
2387
2388 unsigned char
2389 message[MagickPathExtent],
2390 *p;
2391
2392 /*
2393 Delete distributed pixel cache.
2394 */
2395 assert(server_info != (DistributeCacheInfo *) NULL);
2396 assert(server_info->signature == MagickCoreSignature);
2397 p=message;
2398 *p++='d';
2399 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2400 p+=(ptrdiff_t) sizeof(server_info->session_key);
2401 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2402 if (count != (MagickOffsetType) (p-message))
2403 return(MagickFalse);
2404 status=MagickFalse;
2405 count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
2406 if (count != (MagickOffsetType) sizeof(status))
2407 return(MagickFalse);
2408 return(status);
2409}
2410␌
2411/*
2412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2413% %
2414% %
2415% %
2416+ W r i t e D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t %
2417% %
2418% %
2419% %
2420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2421%
2422% WriteDistributePixelCacheMetacontents() writes image metacontent to the
2423% specified region of the distributed pixel cache.
2424%
2425% The format of the WriteDistributePixelCacheMetacontents method is:
2426%
2427% MagickOffsetType WriteDistributePixelCacheMetacontents(
2428% DistributeCacheInfo *server_info,const RectangleInfo *region,
2429% const MagickSizeType length,const unsigned char *metacontent)
2430%
2431% A description of each parameter follows:
2432%
2433% o server_info: the distributed cache info.
2434%
2435% o image: the image.
2436%
2437% o region: write the metacontent to this region of the image.
2438%
2439% o length: the length in bytes of the metacontent.
2440%
2441% o metacontent: write these metacontent to the pixel cache.
2442%
2443*/
2444MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
2445 DistributeCacheInfo *server_info,const RectangleInfo *region,
2446 const MagickSizeType length,const unsigned char *metacontent)
2447{
2448 MagickOffsetType
2449 count;
2450
2451 unsigned char
2452 message[MagickPathExtent],
2453 *p;
2454
2455 /*
2456 Write distributed pixel cache metacontent.
2457 */
2458 assert(server_info != (DistributeCacheInfo *) NULL);
2459 assert(server_info->signature == MagickCoreSignature);
2460 assert(region != (RectangleInfo *) NULL);
2461 assert(metacontent != (unsigned char *) NULL);
2462 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
2463 return(-1);
2464 p=message;
2465 *p++='W';
2466 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2467 p+=(ptrdiff_t) sizeof(server_info->session_key);
2468 (void) memcpy(p,&region->width,sizeof(region->width));
2469 p+=(ptrdiff_t) sizeof(region->width);
2470 (void) memcpy(p,&region->height,sizeof(region->height));
2471 p+=(ptrdiff_t) sizeof(region->height);
2472 (void) memcpy(p,&region->x,sizeof(region->x));
2473 p+=(ptrdiff_t) sizeof(region->x);
2474 (void) memcpy(p,&region->y,sizeof(region->y));
2475 p+=(ptrdiff_t) sizeof(region->y);
2476 (void) memcpy(p,&length,sizeof(length));
2477 p+=(ptrdiff_t) sizeof(length);
2478 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2479 if (count != (MagickOffsetType) (p-message))
2480 return(-1);
2481 return(dpc_send_mac(server_info->file,length,metacontent,
2482 server_info->session_key));
2483}
2484␌
2485/*
2486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2487% %
2488% %
2489% %
2490+ W r i t e D i s t r i b u t e P i x e l C a c h e P i x e l s %
2491% %
2492% %
2493% %
2494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2495%
2496% WriteDistributePixelCachePixels() writes image pixels to the specified
2497% region of the distributed pixel cache.
2498%
2499% The format of the WriteDistributePixelCachePixels method is:
2500%
2501% MagickBooleanType WriteDistributePixelCachePixels(
2502% DistributeCacheInfo *server_info,const RectangleInfo *region,
2503% const MagickSizeType length,
2504% const unsigned char *magick_restrict pixels)
2505%
2506% A description of each parameter follows:
2507%
2508% o server_info: the distributed cache info.
2509%
2510% o image: the image.
2511%
2512% o region: write the pixels to this region of the image.
2513%
2514% o length: the length in bytes of the pixels.
2515%
2516% o pixels: write these pixels to the pixel cache.
2517%
2518*/
2519MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
2520 DistributeCacheInfo *server_info,const RectangleInfo *region,
2521 const MagickSizeType length,const unsigned char *magick_restrict pixels)
2522{
2523 MagickOffsetType
2524 count;
2525
2526 unsigned char
2527 message[MagickPathExtent],
2528 *p;
2529
2530 /*
2531 Write distributed pixel cache pixels.
2532 */
2533 assert(server_info != (DistributeCacheInfo *) NULL);
2534 assert(server_info->signature == MagickCoreSignature);
2535 assert(region != (RectangleInfo *) NULL);
2536 assert(pixels != (const unsigned char *) NULL);
2537 if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
2538 return(-1);
2539 p=message;
2540 *p++='w';
2541 (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2542 p+=(ptrdiff_t) sizeof(server_info->session_key);
2543 (void) memcpy(p,&region->width,sizeof(region->width));
2544 p+=(ptrdiff_t) sizeof(region->width);
2545 (void) memcpy(p,&region->height,sizeof(region->height));
2546 p+=(ptrdiff_t) sizeof(region->height);
2547 (void) memcpy(p,&region->x,sizeof(region->x));
2548 p+=(ptrdiff_t) sizeof(region->x);
2549 (void) memcpy(p,&region->y,sizeof(region->y));
2550 p+=(ptrdiff_t) sizeof(region->y);
2551 (void) memcpy(p,&length,sizeof(length));
2552 p+=(ptrdiff_t) sizeof(length);
2553 count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2554 if (count != (MagickOffsetType) (p-message))
2555 return(-1);
2556 return(dpc_send_mac(server_info->file,length,pixels,
2557 server_info->session_key));
2558}