1/*
2 *
3 * IP-address/hostname to country converter.
4 *
5 * Problem; you want to know where IP a.b.c.d is located.
6 *
7 * Use ares_gethostbyname ("d.c.b.a.zz.countries.nerd.dk")
8 * and get the CNAME (host->h_name). Result will be:
9 *   CNAME = zz<CC>.countries.nerd.dk with address 127.0.x.y (ver 1) or
10 *   CNAME = <a.b.c.d>.zz.countries.nerd.dk with address 127.0.x.y (ver 2)
11 *
12 * The 2 letter country code is in <CC> and the ISO-3166 country
13 * number is in x.y (number = x*256 + y). Version 2 of the protocol is missing
14 * the <CC> number.
15 *
16 * Ref: http://countries.nerd.dk/more.html
17 *
18 * Written by G. Vanem <gvanem@broadpark.no> 2006, 2007
19 *
20 * NB! This program may not be big-endian aware.
21 *
22 * Permission to use, copy, modify, and distribute this
23 * software and its documentation for any purpose and without
24 * fee is hereby granted, provided that the above copyright
25 * notice appear in all copies and that both that copyright
26 * notice and this permission notice appear in supporting
27 * documentation, and that the name of M.I.T. not be used in
28 * advertising or publicity pertaining to distribution of the
29 * software without specific, written prior permission.
30 * M.I.T. makes no representations about the suitability of
31 * this software for any purpose.  It is provided "as is"
32 * without express or implied warranty.
33 */
34
35#include "ares_setup.h"
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdarg.h>
40#include <string.h>
41#include <ctype.h>
42#ifdef HAVE_UNISTD_H
43#include <unistd.h>
44#endif
45#ifdef HAVE_STRINGS_H
46#include <strings.h>
47#endif
48
49#if defined(WIN32) && !defined(WATT32)
50  #include <winsock.h>
51#else
52  #include <sys/socket.h>
53  #include <arpa/inet.h>
54  #include <netinet/in.h>
55  #include <netdb.h>
56#endif
57
58#include "ares.h"
59#include "ares_getopt.h"
60#include "inet_net_pton.h"
61#include "inet_ntop.h"
62#include "ares_nowarn.h"
63
64#ifndef HAVE_STRDUP
65#  include "ares_strdup.h"
66#  define strdup(ptr) ares_strdup(ptr)
67#endif
68
69#ifndef HAVE_STRCASECMP
70#  include "ares_strcasecmp.h"
71#  define strcasecmp(p1,p2) ares_strcasecmp(p1,p2)
72#endif
73
74#ifndef HAVE_STRNCASECMP
75#  include "ares_strcasecmp.h"
76#  define strncasecmp(p1,p2,n) ares_strncasecmp(p1,p2,n)
77#endif
78
79#ifndef INADDR_NONE
80#define INADDR_NONE 0xffffffff
81#endif
82
83static const char *usage      = "acountry [-vh?] {host|addr} ...\n";
84static const char  nerd_fmt[] = "%u.%u.%u.%u.zz.countries.nerd.dk";
85static const char *nerd_ver1  = nerd_fmt + 14;
86static const char *nerd_ver2  = nerd_fmt + 11;
87static int         verbose    = 0;
88
89#define TRACE(fmt) do {               \
90                     if (verbose > 0) \
91                       printf fmt ;   \
92                   } while (0)
93
94static void wait_ares(ares_channel channel);
95static void callback(void *arg, int status, int timeouts, struct hostent *host);
96static void callback2(void *arg, int status, int timeouts, struct hostent *host);
97static void find_country_from_cname(const char *cname, struct in_addr addr);
98
99static void Abort(const char *fmt, ...)
100{
101  va_list args;
102  va_start(args, fmt);
103  vfprintf(stderr, fmt, args);
104  va_end(args);
105  exit(1);
106}
107
108int main(int argc, char **argv)
109{
110  ares_channel channel;
111  int    ch, status;
112
113#if defined(WIN32) && !defined(WATT32)
114  WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK);
115  WSADATA wsaData;
116  WSAStartup(wVersionRequested, &wsaData);
117#endif
118
119  status = ares_library_init(ARES_LIB_INIT_ALL);
120  if (status != ARES_SUCCESS)
121    {
122      fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status));
123      return 1;
124    }
125
126  while ((ch = ares_getopt(argc, argv, "dvh?")) != -1)
127    switch (ch)
128      {
129      case 'd':
130#ifdef WATT32
131        dbug_init();
132#endif
133        break;
134      case 'v':
135        verbose++;
136        break;
137      case 'h':
138      case '?':
139      default:
140        Abort(usage);
141      }
142
143  argc -= optind;
144  argv += optind;
145  if (argc < 1)
146     Abort(usage);
147
148  status = ares_init(&channel);
149  if (status != ARES_SUCCESS)
150    {
151      fprintf(stderr, "ares_init: %s\n", ares_strerror(status));
152      return 1;
153    }
154
155  /* Initiate the queries, one per command-line argument. */
156  for ( ; *argv; argv++)
157    {
158      struct in_addr addr;
159      char buf[100];
160
161      /* If this fails, assume '*argv' is a host-name that
162       * must be resolved first
163       */
164      if (ares_inet_pton(AF_INET, *argv, &addr) != 1)
165        {
166          ares_gethostbyname(channel, *argv, AF_INET, callback2, &addr);
167          wait_ares(channel);
168          if (addr.s_addr == INADDR_NONE)
169            {
170              printf("Failed to lookup %s\n", *argv);
171              continue;
172            }
173        }
174
175      sprintf(buf, nerd_fmt,
176              (unsigned int)(addr.s_addr >> 24),
177              (unsigned int)((addr.s_addr >> 16) & 255),
178              (unsigned int)((addr.s_addr >> 8) & 255),
179              (unsigned int)(addr.s_addr & 255));
180      TRACE(("Looking up %s...", buf));
181      fflush(stdout);
182      ares_gethostbyname(channel, buf, AF_INET, callback, buf);
183    }
184
185  wait_ares(channel);
186  ares_destroy(channel);
187
188  ares_library_cleanup();
189
190#if defined(WIN32) && !defined(WATT32)
191  WSACleanup();
192#endif
193
194  return 0;
195}
196
197/*
198 * Wait for the queries to complete.
199 */
200static void wait_ares(ares_channel channel)
201{
202  for (;;)
203    {
204      struct timeval *tvp, tv;
205      fd_set read_fds, write_fds;
206      int nfds;
207
208      FD_ZERO(&read_fds);
209      FD_ZERO(&write_fds);
210      nfds = ares_fds(channel, &read_fds, &write_fds);
211      if (nfds == 0)
212        break;
213      tvp = ares_timeout(channel, NULL, &tv);
214      select(nfds, &read_fds, &write_fds, NULL, tvp);
215      ares_process(channel, &read_fds, &write_fds);
216    }
217}
218
219/*
220 * This is the callback used when we have the IP-address of interest.
221 * Extract the CNAME and figure out the country-code from it.
222 */
223static void callback(void *arg, int status, int timeouts, struct hostent *host)
224{
225  const char *name = (const char*)arg;
226  const char *cname;
227  char buf[20];
228
229  (void)timeouts;
230
231  if (!host || status != ARES_SUCCESS)
232    {
233      printf("Failed to lookup %s: %s\n", name, ares_strerror(status));
234      return;
235    }
236
237  TRACE(("\nFound address %s, name %s\n",
238         ares_inet_ntop(AF_INET,(const char*)host->h_addr,buf,sizeof(buf)),
239         host->h_name));
240
241  cname = host->h_name;  /* CNAME gets put here */
242  if (!cname)
243    printf("Failed to get CNAME for %s\n", name);
244  else
245    find_country_from_cname(cname, *(struct in_addr*)host->h_addr);
246}
247
248/*
249 * This is the callback used to obtain the IP-address of the host of interest.
250 */
251static void callback2(void *arg, int status, int timeouts, struct hostent *host)
252{
253  struct in_addr *addr = (struct in_addr*) arg;
254
255  (void)timeouts;
256  if (!host || status != ARES_SUCCESS)
257    memset(addr, INADDR_NONE, sizeof(*addr));
258  else
259    memcpy(addr, host->h_addr, sizeof(*addr));
260}
261
262struct search_list {
263       int         country_number; /* ISO-3166 country number */
264       char        short_name[3];  /* A2 short country code */
265       const char *long_name;      /* normal country name */
266     };
267
268static const struct search_list *list_lookup(int number, const struct search_list *list, int num)
269{
270  while (num > 0 && list->long_name)
271    {
272      if (list->country_number == number)
273        return (list);
274      num--;
275      list++;
276    }
277  return (NULL);
278}
279
280/*
281 * Ref: ftp://ftp.ripe.net/iso3166-countrycodes.txt
282 */
283static const struct search_list country_list[] = {
284       {   4, "af", "Afghanistan"                          },
285       { 248, "ax", "�land Island"                         },
286       {   8, "al", "Albania"                              },
287       {  12, "dz", "Algeria"                              },
288       {  16, "as", "American Samoa"                       },
289       {  20, "ad", "Andorra"                              },
290       {  24, "ao", "Angola"                               },
291       { 660, "ai", "Anguilla"                             },
292       {  10, "aq", "Antarctica"                           },
293       {  28, "ag", "Antigua & Barbuda"                    },
294       {  32, "ar", "Argentina"                            },
295       {  51, "am", "Armenia"                              },
296       { 533, "aw", "Aruba"                                },
297       {  36, "au", "Australia"                            },
298       {  40, "at", "Austria"                              },
299       {  31, "az", "Azerbaijan"                           },
300       {  44, "bs", "Bahamas"                              },
301       {  48, "bh", "Bahrain"                              },
302       {  50, "bd", "Bangladesh"                           },
303       {  52, "bb", "Barbados"                             },
304       { 112, "by", "Belarus"                              },
305       {  56, "be", "Belgium"                              },
306       {  84, "bz", "Belize"                               },
307       { 204, "bj", "Benin"                                },
308       {  60, "bm", "Bermuda"                              },
309       {  64, "bt", "Bhutan"                               },
310       {  68, "bo", "Bolivia"                              },
311       {  70, "ba", "Bosnia & Herzegowina"                 },
312       {  72, "bw", "Botswana"                             },
313       {  74, "bv", "Bouvet Island"                        },
314       {  76, "br", "Brazil"                               },
315       {  86, "io", "British Indian Ocean Territory"       },
316       {  96, "bn", "Brunei Darussalam"                    },
317       { 100, "bg", "Bulgaria"                             },
318       { 854, "bf", "Burkina Faso"                         },
319       { 108, "bi", "Burundi"                              },
320       { 116, "kh", "Cambodia"                             },
321       { 120, "cm", "Cameroon"                             },
322       { 124, "ca", "Canada"                               },
323       { 132, "cv", "Cape Verde"                           },
324       { 136, "ky", "Cayman Islands"                       },
325       { 140, "cf", "Central African Republic"             },
326       { 148, "td", "Chad"                                 },
327       { 152, "cl", "Chile"                                },
328       { 156, "cn", "China"                                },
329       { 162, "cx", "Christmas Island"                     },
330       { 166, "cc", "Cocos Islands"                        },
331       { 170, "co", "Colombia"                             },
332       { 174, "km", "Comoros"                              },
333       { 178, "cg", "Congo"                                },
334       { 180, "cd", "Congo"                                },
335       { 184, "ck", "Cook Islands"                         },
336       { 188, "cr", "Costa Rica"                           },
337       { 384, "ci", "Cote d'Ivoire"                        },
338       { 191, "hr", "Croatia"                              },
339       { 192, "cu", "Cuba"                                 },
340       { 196, "cy", "Cyprus"                               },
341       { 203, "cz", "Czech Republic"                       },
342       { 208, "dk", "Denmark"                              },
343       { 262, "dj", "Djibouti"                             },
344       { 212, "dm", "Dominica"                             },
345       { 214, "do", "Dominican Republic"                   },
346       { 218, "ec", "Ecuador"                              },
347       { 818, "eg", "Egypt"                                },
348       { 222, "sv", "El Salvador"                          },
349       { 226, "gq", "Equatorial Guinea"                    },
350       { 232, "er", "Eritrea"                              },
351       { 233, "ee", "Estonia"                              },
352       { 231, "et", "Ethiopia"                             },
353       { 238, "fk", "Falkland Islands"                     },
354       { 234, "fo", "Faroe Islands"                        },
355       { 242, "fj", "Fiji"                                 },
356       { 246, "fi", "Finland"                              },
357       { 250, "fr", "France"                               },
358       { 249, "fx", "France, Metropolitan"                 },
359       { 254, "gf", "French Guiana"                        },
360       { 258, "pf", "French Polynesia"                     },
361       { 260, "tf", "French Southern Territories"          },
362       { 266, "ga", "Gabon"                                },
363       { 270, "gm", "Gambia"                               },
364       { 268, "ge", "Georgia"                              },
365       { 276, "de", "Germany"                              },
366       { 288, "gh", "Ghana"                                },
367       { 292, "gi", "Gibraltar"                            },
368       { 300, "gr", "Greece"                               },
369       { 304, "gl", "Greenland"                            },
370       { 308, "gd", "Grenada"                              },
371       { 312, "gp", "Guadeloupe"                           },
372       { 316, "gu", "Guam"                                 },
373       { 320, "gt", "Guatemala"                            },
374       { 324, "gn", "Guinea"                               },
375       { 624, "gw", "Guinea-Bissau"                        },
376       { 328, "gy", "Guyana"                               },
377       { 332, "ht", "Haiti"                                },
378       { 334, "hm", "Heard & Mc Donald Islands"            },
379       { 336, "va", "Vatican City"                         },
380       { 340, "hn", "Honduras"                             },
381       { 344, "hk", "Hong kong"                            },
382       { 348, "hu", "Hungary"                              },
383       { 352, "is", "Iceland"                              },
384       { 356, "in", "India"                                },
385       { 360, "id", "Indonesia"                            },
386       { 364, "ir", "Iran"                                 },
387       { 368, "iq", "Iraq"                                 },
388       { 372, "ie", "Ireland"                              },
389       { 376, "il", "Israel"                               },
390       { 380, "it", "Italy"                                },
391       { 388, "jm", "Jamaica"                              },
392       { 392, "jp", "Japan"                                },
393       { 400, "jo", "Jordan"                               },
394       { 398, "kz", "Kazakhstan"                           },
395       { 404, "ke", "Kenya"                                },
396       { 296, "ki", "Kiribati"                             },
397       { 408, "kp", "Korea (north)"                        },
398       { 410, "kr", "Korea (south)"                        },
399       { 414, "kw", "Kuwait"                               },
400       { 417, "kg", "Kyrgyzstan"                           },
401       { 418, "la", "Laos"                                 },
402       { 428, "lv", "Latvia"                               },
403       { 422, "lb", "Lebanon"                              },
404       { 426, "ls", "Lesotho"                              },
405       { 430, "lr", "Liberia"                              },
406       { 434, "ly", "Libya"                                },
407       { 438, "li", "Liechtenstein"                        },
408       { 440, "lt", "Lithuania"                            },
409       { 442, "lu", "Luxembourg"                           },
410       { 446, "mo", "Macao"                                },
411       { 807, "mk", "Macedonia"                            },
412       { 450, "mg", "Madagascar"                           },
413       { 454, "mw", "Malawi"                               },
414       { 458, "my", "Malaysia"                             },
415       { 462, "mv", "Maldives"                             },
416       { 466, "ml", "Mali"                                 },
417       { 470, "mt", "Malta"                                },
418       { 584, "mh", "Marshall Islands"                     },
419       { 474, "mq", "Martinique"                           },
420       { 478, "mr", "Mauritania"                           },
421       { 480, "mu", "Mauritius"                            },
422       { 175, "yt", "Mayotte"                              },
423       { 484, "mx", "Mexico"                               },
424       { 583, "fm", "Micronesia"                           },
425       { 498, "md", "Moldova"                              },
426       { 492, "mc", "Monaco"                               },
427       { 496, "mn", "Mongolia"                             },
428       { 500, "ms", "Montserrat"                           },
429       { 504, "ma", "Morocco"                              },
430       { 508, "mz", "Mozambique"                           },
431       { 104, "mm", "Myanmar"                              },
432       { 516, "na", "Namibia"                              },
433       { 520, "nr", "Nauru"                                },
434       { 524, "np", "Nepal"                                },
435       { 528, "nl", "Netherlands"                          },
436       { 530, "an", "Netherlands Antilles"                 },
437       { 540, "nc", "New Caledonia"                        },
438       { 554, "nz", "New Zealand"                          },
439       { 558, "ni", "Nicaragua"                            },
440       { 562, "ne", "Niger"                                },
441       { 566, "ng", "Nigeria"                              },
442       { 570, "nu", "Niue"                                 },
443       { 574, "nf", "Norfolk Island"                       },
444       { 580, "mp", "Northern Mariana Islands"             },
445       { 578, "no", "Norway"                               },
446       { 512, "om", "Oman"                                 },
447       { 586, "pk", "Pakistan"                             },
448       { 585, "pw", "Palau"                                },
449       { 275, "ps", "Palestinian Territory"                },
450       { 591, "pa", "Panama"                               },
451       { 598, "pg", "Papua New Guinea"                     },
452       { 600, "py", "Paraguay"                             },
453       { 604, "pe", "Peru"                                 },
454       { 608, "ph", "Philippines"                          },
455       { 612, "pn", "Pitcairn"                             },
456       { 616, "pl", "Poland"                               },
457       { 620, "pt", "Portugal"                             },
458       { 630, "pr", "Puerto Rico"                          },
459       { 634, "qa", "Qatar"                                },
460       { 638, "re", "Reunion"                              },
461       { 642, "ro", "Romania"                              },
462       { 643, "ru", "Russia"                               },
463       { 646, "rw", "Rwanda"                               },
464       { 659, "kn", "Saint Kitts & Nevis"                  },
465       { 662, "lc", "Saint Lucia"                          },
466       { 670, "vc", "Saint Vincent"                        },
467       { 882, "ws", "Samoa"                                },
468       { 674, "sm", "San Marino"                           },
469       { 678, "st", "Sao Tome & Principe"                  },
470       { 682, "sa", "Saudi Arabia"                         },
471       { 686, "sn", "Senegal"                              },
472       { 891, "cs", "Serbia and Montenegro"                },
473       { 690, "sc", "Seychelles"                           },
474       { 694, "sl", "Sierra Leone"                         },
475       { 702, "sg", "Singapore"                            },
476       { 703, "sk", "Slovakia"                             },
477       { 705, "si", "Slovenia"                             },
478       {  90, "sb", "Solomon Islands"                      },
479       { 706, "so", "Somalia"                              },
480       { 710, "za", "South Africa"                         },
481       { 239, "gs", "South Georgia"                        },
482       { 724, "es", "Spain"                                },
483       { 144, "lk", "Sri Lanka"                            },
484       { 654, "sh", "St. Helena"                           },
485       { 666, "pm", "St. Pierre & Miquelon"                },
486       { 736, "sd", "Sudan"                                },
487       { 740, "sr", "Suriname"                             },
488       { 744, "sj", "Svalbard & Jan Mayen Islands"         },
489       { 748, "sz", "Swaziland"                            },
490       { 752, "se", "Sweden"                               },
491       { 756, "ch", "Switzerland"                          },
492       { 760, "sy", "Syrian Arab Republic"                 },
493       { 626, "tl", "Timor-Leste"                          },
494       { 158, "tw", "Taiwan"                               },
495       { 762, "tj", "Tajikistan"                           },
496       { 834, "tz", "Tanzania"                             },
497       { 764, "th", "Thailand"                             },
498       { 768, "tg", "Togo"                                 },
499       { 772, "tk", "Tokelau"                              },
500       { 776, "to", "Tonga"                                },
501       { 780, "tt", "Trinidad & Tobago"                    },
502       { 788, "tn", "Tunisia"                              },
503       { 792, "tr", "Turkey"                               },
504       { 795, "tm", "Turkmenistan"                         },
505       { 796, "tc", "Turks & Caicos Islands"               },
506       { 798, "tv", "Tuvalu"                               },
507       { 800, "ug", "Uganda"                               },
508       { 804, "ua", "Ukraine"                              },
509       { 784, "ae", "United Arab Emirates"                 },
510       { 826, "gb", "United Kingdom"                       },
511       { 840, "us", "United States"                        },
512       { 581, "um", "United States Minor Outlying Islands" },
513       { 858, "uy", "Uruguay"                              },
514       { 860, "uz", "Uzbekistan"                           },
515       { 548, "vu", "Vanuatu"                              },
516       { 862, "ve", "Venezuela"                            },
517       { 704, "vn", "Vietnam"                              },
518       {  92, "vg", "Virgin Islands (British)"             },
519       { 850, "vi", "Virgin Islands (US)"                  },
520       { 876, "wf", "Wallis & Futuna Islands"              },
521       { 732, "eh", "Western Sahara"                       },
522       { 887, "ye", "Yemen"                                },
523       { 894, "zm", "Zambia"                               },
524       { 716, "zw", "Zimbabwe"                             }
525     };
526
527/*
528 * Check if start of 'str' is simply an IPv4 address.
529 */
530#define BYTE_OK(x) ((x) >= 0 && (x) <= 255)
531
532static int is_addr(char *str, char **end)
533{
534  int a0, a1, a2, a3, num, rc = 0, length = 0;
535
536  num = sscanf(str,"%3d.%3d.%3d.%3d%n",&a0,&a1,&a2,&a3,&length);
537  if( (num == 4) &&
538      BYTE_OK(a0) && BYTE_OK(a1) && BYTE_OK(a2) && BYTE_OK(a3) &&
539      length >= (3+4))
540    {
541      rc = 1;
542      *end = str + length;
543    }
544  return rc;
545}
546
547/*
548 * Find the country-code and name from the CNAME. E.g.:
549 *   version 1: CNAME = zzno.countries.nerd.dk with address 127.0.2.66
550 *              yields ccode_A" = "no" and cnumber 578 (2.66).
551 *   version 2: CNAME = <a.b.c.d>.zz.countries.nerd.dk with address 127.0.2.66
552 *              yields cnumber 578 (2.66). ccode_A is "";
553 */
554static void find_country_from_cname(const char *cname, struct in_addr addr)
555{
556  const struct search_list *country;
557  char  ccode_A2[3], *ccopy, *dot_4;
558  int   cnumber, z0, z1, ver_1, ver_2;
559  unsigned long ip;
560
561  ip = ntohl(addr.s_addr);
562  z0 = TOLOWER(cname[0]);
563  z1 = TOLOWER(cname[1]);
564  ccopy = strdup(cname);
565  dot_4 = NULL;
566
567  ver_1 = (z0 == 'z' && z1 == 'z' && !strcasecmp(cname+4,nerd_ver1));
568  ver_2 = (is_addr(ccopy,&dot_4) && !strcasecmp(dot_4,nerd_ver2));
569
570  if (ver_1)
571    {
572      const char *dot = strchr(cname, '.');
573      if ((z0 != 'z' && z1 != 'z') || dot != cname+4)
574        {
575          printf("Unexpected CNAME %s (ver_1)\n", cname);
576          return;
577        }
578    }
579  else if (ver_2)
580    {
581      z0 = TOLOWER(dot_4[1]);
582      z1 = TOLOWER(dot_4[2]);
583      if (z0 != 'z' && z1 != 'z')
584        {
585          printf("Unexpected CNAME %s (ver_2)\n", cname);
586          return;
587        }
588    }
589  else
590    {
591      printf("Unexpected CNAME %s (ver?)\n", cname);
592      return;
593    }
594
595  if (ver_1)
596    {
597      ccode_A2[0] = (char)TOLOWER(cname[2]);
598      ccode_A2[1] = (char)TOLOWER(cname[3]);
599      ccode_A2[2] = '\0';
600    }
601  else
602    ccode_A2[0] = '\0';
603
604  cnumber = ip & 0xFFFF;
605
606  TRACE(("Found country-code `%s', number %d\n",
607         ver_1 ? ccode_A2 : "<n/a>", cnumber));
608
609  country = list_lookup(cnumber, country_list,
610                        sizeof(country_list) / sizeof(country_list[0]));
611  if (!country)
612    printf("Name for country-number %d not found.\n", cnumber);
613  else
614    {
615      if (ver_1)
616        {
617          if ((country->short_name[0] != ccode_A2[0]) ||
618              (country->short_name[1] != ccode_A2[1]) ||
619              (country->short_name[2] != ccode_A2[2]))
620            printf("short-name mismatch; %s vs %s\n",
621                   country->short_name, ccode_A2);
622        }
623      printf("%s (%s), number %d.\n",
624             country->long_name, country->short_name, cnumber);
625    }
626  free(ccopy);
627}
628
629