Preliminary main mbed library for nexpaq development

Committer:
nexpaq
Date:
Fri Nov 04 20:27:58 2016 +0000
Revision:
0:6c56fb4bc5f0
Moving to library for sharing updates

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nexpaq 0:6c56fb4bc5f0 1 /* nsapi_dns.cpp
nexpaq 0:6c56fb4bc5f0 2 * Original work Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
nexpaq 0:6c56fb4bc5f0 3 * Modified work Copyright (c) 2015 ARM Limited
nexpaq 0:6c56fb4bc5f0 4 *
nexpaq 0:6c56fb4bc5f0 5 * Licensed under the Apache License, Version 2.0 (the "License");
nexpaq 0:6c56fb4bc5f0 6 * you may not use this file except in compliance with the License.
nexpaq 0:6c56fb4bc5f0 7 * You may obtain a copy of the License at
nexpaq 0:6c56fb4bc5f0 8 *
nexpaq 0:6c56fb4bc5f0 9 * http://www.apache.org/licenses/LICENSE-2.0
nexpaq 0:6c56fb4bc5f0 10 *
nexpaq 0:6c56fb4bc5f0 11 * Unless required by applicable law or agreed to in writing, software
nexpaq 0:6c56fb4bc5f0 12 * distributed under the License is distributed on an "AS IS" BASIS,
nexpaq 0:6c56fb4bc5f0 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
nexpaq 0:6c56fb4bc5f0 14 * See the License for the specific language governing permissions and
nexpaq 0:6c56fb4bc5f0 15 * limitations under the License.
nexpaq 0:6c56fb4bc5f0 16 */
nexpaq 0:6c56fb4bc5f0 17 #include "nsapi_dns.h"
nexpaq 0:6c56fb4bc5f0 18 #include "network-socket/UDPSocket.h"
nexpaq 0:6c56fb4bc5f0 19 #include <string.h>
nexpaq 0:6c56fb4bc5f0 20 #include <stdlib.h>
nexpaq 0:6c56fb4bc5f0 21
nexpaq 0:6c56fb4bc5f0 22
nexpaq 0:6c56fb4bc5f0 23 // DNS options
nexpaq 0:6c56fb4bc5f0 24 #define DNS_BUFFER_SIZE 256
nexpaq 0:6c56fb4bc5f0 25 #define DNS_TIMEOUT 5000
nexpaq 0:6c56fb4bc5f0 26 #define DNS_SERVERS_SIZE 5
nexpaq 0:6c56fb4bc5f0 27
nexpaq 0:6c56fb4bc5f0 28 nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
nexpaq 0:6c56fb4bc5f0 29 {NSAPI_IPv4, {8, 8, 8, 8}},
nexpaq 0:6c56fb4bc5f0 30 {NSAPI_IPv4, {209, 244, 0, 3}},
nexpaq 0:6c56fb4bc5f0 31 {NSAPI_IPv4, {84, 200, 69, 80}},
nexpaq 0:6c56fb4bc5f0 32 {NSAPI_IPv4, {8, 26, 56, 26}},
nexpaq 0:6c56fb4bc5f0 33 {NSAPI_IPv4, {208, 67, 222, 222}},
nexpaq 0:6c56fb4bc5f0 34 };
nexpaq 0:6c56fb4bc5f0 35
nexpaq 0:6c56fb4bc5f0 36
nexpaq 0:6c56fb4bc5f0 37 // DNS server configuration
nexpaq 0:6c56fb4bc5f0 38 extern "C" int nsapi_dns_add_server(nsapi_addr_t addr)
nexpaq 0:6c56fb4bc5f0 39 {
nexpaq 0:6c56fb4bc5f0 40 memmove(&dns_servers[1], &dns_servers[0],
nexpaq 0:6c56fb4bc5f0 41 (DNS_SERVERS_SIZE-1)*sizeof(nsapi_addr_t));
nexpaq 0:6c56fb4bc5f0 42
nexpaq 0:6c56fb4bc5f0 43 dns_servers[0] = addr;
nexpaq 0:6c56fb4bc5f0 44 return 0;
nexpaq 0:6c56fb4bc5f0 45 }
nexpaq 0:6c56fb4bc5f0 46
nexpaq 0:6c56fb4bc5f0 47
nexpaq 0:6c56fb4bc5f0 48 // DNS packet parsing
nexpaq 0:6c56fb4bc5f0 49 static void dns_append_byte(uint8_t **p, uint8_t byte)
nexpaq 0:6c56fb4bc5f0 50 {
nexpaq 0:6c56fb4bc5f0 51 *(*p)++ = byte;
nexpaq 0:6c56fb4bc5f0 52 }
nexpaq 0:6c56fb4bc5f0 53
nexpaq 0:6c56fb4bc5f0 54 static void dns_append_word(uint8_t **p, uint16_t word)
nexpaq 0:6c56fb4bc5f0 55 {
nexpaq 0:6c56fb4bc5f0 56
nexpaq 0:6c56fb4bc5f0 57 dns_append_byte(p, 0xff & (word >> 8));
nexpaq 0:6c56fb4bc5f0 58 dns_append_byte(p, 0xff & (word >> 0));
nexpaq 0:6c56fb4bc5f0 59 }
nexpaq 0:6c56fb4bc5f0 60
nexpaq 0:6c56fb4bc5f0 61 static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
nexpaq 0:6c56fb4bc5f0 62 {
nexpaq 0:6c56fb4bc5f0 63 dns_append_byte(p, len);
nexpaq 0:6c56fb4bc5f0 64 memcpy(*p, name, len);
nexpaq 0:6c56fb4bc5f0 65 *p += len;
nexpaq 0:6c56fb4bc5f0 66 }
nexpaq 0:6c56fb4bc5f0 67
nexpaq 0:6c56fb4bc5f0 68 static uint8_t dns_scan_byte(const uint8_t **p)
nexpaq 0:6c56fb4bc5f0 69 {
nexpaq 0:6c56fb4bc5f0 70 return *(*p)++;
nexpaq 0:6c56fb4bc5f0 71 }
nexpaq 0:6c56fb4bc5f0 72
nexpaq 0:6c56fb4bc5f0 73 static uint16_t dns_scan_word(const uint8_t **p)
nexpaq 0:6c56fb4bc5f0 74 {
nexpaq 0:6c56fb4bc5f0 75 uint16_t a = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 76 uint16_t b = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 77 return (a << 8) | b;
nexpaq 0:6c56fb4bc5f0 78 }
nexpaq 0:6c56fb4bc5f0 79
nexpaq 0:6c56fb4bc5f0 80
nexpaq 0:6c56fb4bc5f0 81 static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 82 {
nexpaq 0:6c56fb4bc5f0 83 // fill the header
nexpaq 0:6c56fb4bc5f0 84 dns_append_word(p, 1); // id = 1
nexpaq 0:6c56fb4bc5f0 85 dns_append_word(p, 0x0100); // flags = recursion required
nexpaq 0:6c56fb4bc5f0 86 dns_append_word(p, 1); // qdcount = 1
nexpaq 0:6c56fb4bc5f0 87 dns_append_word(p, 0); // ancount = 0
nexpaq 0:6c56fb4bc5f0 88 dns_append_word(p, 0); // nscount = 0
nexpaq 0:6c56fb4bc5f0 89 dns_append_word(p, 0); // arcount = 0
nexpaq 0:6c56fb4bc5f0 90
nexpaq 0:6c56fb4bc5f0 91 // fill out the question names
nexpaq 0:6c56fb4bc5f0 92 while (host[0]) {
nexpaq 0:6c56fb4bc5f0 93 size_t label_len = strcspn(host, ".");
nexpaq 0:6c56fb4bc5f0 94 dns_append_name(p, host, label_len);
nexpaq 0:6c56fb4bc5f0 95 host += label_len + (host[label_len] == '.');
nexpaq 0:6c56fb4bc5f0 96 }
nexpaq 0:6c56fb4bc5f0 97
nexpaq 0:6c56fb4bc5f0 98 dns_append_byte(p, 0);
nexpaq 0:6c56fb4bc5f0 99
nexpaq 0:6c56fb4bc5f0 100 // fill out question footer
nexpaq 0:6c56fb4bc5f0 101 if (version == NSAPI_IPv4) {
nexpaq 0:6c56fb4bc5f0 102 dns_append_word(p, 1); // qtype = ipv4
nexpaq 0:6c56fb4bc5f0 103 } else {
nexpaq 0:6c56fb4bc5f0 104 dns_append_word(p, 28); // qtype = ipv6
nexpaq 0:6c56fb4bc5f0 105 }
nexpaq 0:6c56fb4bc5f0 106 dns_append_word(p, 1); // qclass = 1
nexpaq 0:6c56fb4bc5f0 107 }
nexpaq 0:6c56fb4bc5f0 108
nexpaq 0:6c56fb4bc5f0 109 static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
nexpaq 0:6c56fb4bc5f0 110 {
nexpaq 0:6c56fb4bc5f0 111 // scan header
nexpaq 0:6c56fb4bc5f0 112 uint16_t id = dns_scan_word(p);
nexpaq 0:6c56fb4bc5f0 113 uint16_t flags = dns_scan_word(p);
nexpaq 0:6c56fb4bc5f0 114 bool qr = 0x1 & (flags >> 15);
nexpaq 0:6c56fb4bc5f0 115 uint8_t opcode = 0xf & (flags >> 11);
nexpaq 0:6c56fb4bc5f0 116 uint8_t rcode = 0xf & (flags >> 0);
nexpaq 0:6c56fb4bc5f0 117
nexpaq 0:6c56fb4bc5f0 118 uint16_t qdcount = dns_scan_word(p); // qdcount
nexpaq 0:6c56fb4bc5f0 119 uint16_t ancount = dns_scan_word(p); // ancount
nexpaq 0:6c56fb4bc5f0 120 dns_scan_word(p); // nscount
nexpaq 0:6c56fb4bc5f0 121 dns_scan_word(p); // arcount
nexpaq 0:6c56fb4bc5f0 122
nexpaq 0:6c56fb4bc5f0 123 // verify header is response to query
nexpaq 0:6c56fb4bc5f0 124 if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
nexpaq 0:6c56fb4bc5f0 125 return 0;
nexpaq 0:6c56fb4bc5f0 126 }
nexpaq 0:6c56fb4bc5f0 127
nexpaq 0:6c56fb4bc5f0 128 // skip questions
nexpaq 0:6c56fb4bc5f0 129 for (int i = 0; i < qdcount; i++) {
nexpaq 0:6c56fb4bc5f0 130 while (true) {
nexpaq 0:6c56fb4bc5f0 131 uint8_t len = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 132 if (len == 0) {
nexpaq 0:6c56fb4bc5f0 133 break;
nexpaq 0:6c56fb4bc5f0 134 }
nexpaq 0:6c56fb4bc5f0 135
nexpaq 0:6c56fb4bc5f0 136 *p += len;
nexpaq 0:6c56fb4bc5f0 137 }
nexpaq 0:6c56fb4bc5f0 138
nexpaq 0:6c56fb4bc5f0 139 dns_scan_word(p); // qtype
nexpaq 0:6c56fb4bc5f0 140 dns_scan_word(p); // qclass
nexpaq 0:6c56fb4bc5f0 141 }
nexpaq 0:6c56fb4bc5f0 142
nexpaq 0:6c56fb4bc5f0 143 // scan each response
nexpaq 0:6c56fb4bc5f0 144 unsigned count = 0;
nexpaq 0:6c56fb4bc5f0 145
nexpaq 0:6c56fb4bc5f0 146 for (int i = 0; i < ancount && count < addr_count; i++) {
nexpaq 0:6c56fb4bc5f0 147 while (true) {
nexpaq 0:6c56fb4bc5f0 148 uint8_t len = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 149 if (len == 0) {
nexpaq 0:6c56fb4bc5f0 150 break;
nexpaq 0:6c56fb4bc5f0 151 } else if (len & 0xc0) { // this is link
nexpaq 0:6c56fb4bc5f0 152 dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 153 break;
nexpaq 0:6c56fb4bc5f0 154 }
nexpaq 0:6c56fb4bc5f0 155
nexpaq 0:6c56fb4bc5f0 156 *p += len;
nexpaq 0:6c56fb4bc5f0 157 }
nexpaq 0:6c56fb4bc5f0 158
nexpaq 0:6c56fb4bc5f0 159 uint16_t rtype = dns_scan_word(p); // rtype
nexpaq 0:6c56fb4bc5f0 160 uint16_t rclass = dns_scan_word(p); // rclass
nexpaq 0:6c56fb4bc5f0 161 *p += 4; // ttl
nexpaq 0:6c56fb4bc5f0 162 uint16_t rdlength = dns_scan_word(p); // rdlength
nexpaq 0:6c56fb4bc5f0 163
nexpaq 0:6c56fb4bc5f0 164 if (rtype == 1 && rclass == 1 && rdlength == NSAPI_IPv4_BYTES) {
nexpaq 0:6c56fb4bc5f0 165 // accept A record
nexpaq 0:6c56fb4bc5f0 166 addr->version = NSAPI_IPv4;
nexpaq 0:6c56fb4bc5f0 167 for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
nexpaq 0:6c56fb4bc5f0 168 addr->bytes[i] = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 169 }
nexpaq 0:6c56fb4bc5f0 170
nexpaq 0:6c56fb4bc5f0 171 addr += 1;
nexpaq 0:6c56fb4bc5f0 172 count += 1;
nexpaq 0:6c56fb4bc5f0 173 } else if (rtype == 28 && rclass == 1 && rdlength == NSAPI_IPv6_BYTES) {
nexpaq 0:6c56fb4bc5f0 174 // accept AAAA record
nexpaq 0:6c56fb4bc5f0 175 addr->version = NSAPI_IPv6;
nexpaq 0:6c56fb4bc5f0 176 for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
nexpaq 0:6c56fb4bc5f0 177 addr->bytes[i] = dns_scan_byte(p);
nexpaq 0:6c56fb4bc5f0 178 }
nexpaq 0:6c56fb4bc5f0 179
nexpaq 0:6c56fb4bc5f0 180 addr += 1;
nexpaq 0:6c56fb4bc5f0 181 count += 1;
nexpaq 0:6c56fb4bc5f0 182 } else {
nexpaq 0:6c56fb4bc5f0 183 // skip unrecognized records
nexpaq 0:6c56fb4bc5f0 184 *p += rdlength;
nexpaq 0:6c56fb4bc5f0 185 }
nexpaq 0:6c56fb4bc5f0 186 }
nexpaq 0:6c56fb4bc5f0 187
nexpaq 0:6c56fb4bc5f0 188 return count;
nexpaq 0:6c56fb4bc5f0 189 }
nexpaq 0:6c56fb4bc5f0 190
nexpaq 0:6c56fb4bc5f0 191 // core query function
nexpaq 0:6c56fb4bc5f0 192 static int nsapi_dns_query_multiple(NetworkStack *stack,
nexpaq 0:6c56fb4bc5f0 193 nsapi_addr_t *addr, unsigned addr_count,
nexpaq 0:6c56fb4bc5f0 194 const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 195 {
nexpaq 0:6c56fb4bc5f0 196 // check for valid host name
nexpaq 0:6c56fb4bc5f0 197 int host_len = host ? strlen(host) : 0;
nexpaq 0:6c56fb4bc5f0 198 if (host_len > 128 || host_len == 0) {
nexpaq 0:6c56fb4bc5f0 199 return NSAPI_ERROR_PARAMETER;
nexpaq 0:6c56fb4bc5f0 200 }
nexpaq 0:6c56fb4bc5f0 201
nexpaq 0:6c56fb4bc5f0 202 // create a udp socket
nexpaq 0:6c56fb4bc5f0 203 UDPSocket socket;
nexpaq 0:6c56fb4bc5f0 204 int err = socket.open(stack);
nexpaq 0:6c56fb4bc5f0 205 if (err) {
nexpaq 0:6c56fb4bc5f0 206 return err;
nexpaq 0:6c56fb4bc5f0 207 }
nexpaq 0:6c56fb4bc5f0 208
nexpaq 0:6c56fb4bc5f0 209 socket.set_timeout(DNS_TIMEOUT);
nexpaq 0:6c56fb4bc5f0 210
nexpaq 0:6c56fb4bc5f0 211 // create network packet
nexpaq 0:6c56fb4bc5f0 212 uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
nexpaq 0:6c56fb4bc5f0 213 if (!packet) {
nexpaq 0:6c56fb4bc5f0 214 return NSAPI_ERROR_NO_MEMORY;
nexpaq 0:6c56fb4bc5f0 215 }
nexpaq 0:6c56fb4bc5f0 216
nexpaq 0:6c56fb4bc5f0 217 int result = NSAPI_ERROR_DNS_FAILURE;
nexpaq 0:6c56fb4bc5f0 218
nexpaq 0:6c56fb4bc5f0 219 // check against each dns server
nexpaq 0:6c56fb4bc5f0 220 for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
nexpaq 0:6c56fb4bc5f0 221 // send the question
nexpaq 0:6c56fb4bc5f0 222 uint8_t *p = packet;
nexpaq 0:6c56fb4bc5f0 223 dns_append_question(&p, host, version);
nexpaq 0:6c56fb4bc5f0 224
nexpaq 0:6c56fb4bc5f0 225 err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, DNS_BUFFER_SIZE);
nexpaq 0:6c56fb4bc5f0 226 if (err == NSAPI_ERROR_WOULD_BLOCK) {
nexpaq 0:6c56fb4bc5f0 227 continue;
nexpaq 0:6c56fb4bc5f0 228 } else if (err < 0) {
nexpaq 0:6c56fb4bc5f0 229 result = err;
nexpaq 0:6c56fb4bc5f0 230 break;
nexpaq 0:6c56fb4bc5f0 231 }
nexpaq 0:6c56fb4bc5f0 232
nexpaq 0:6c56fb4bc5f0 233 // recv the response
nexpaq 0:6c56fb4bc5f0 234 err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
nexpaq 0:6c56fb4bc5f0 235 if (err == NSAPI_ERROR_WOULD_BLOCK) {
nexpaq 0:6c56fb4bc5f0 236 continue;
nexpaq 0:6c56fb4bc5f0 237 } else if (err < 0) {
nexpaq 0:6c56fb4bc5f0 238 result = err;
nexpaq 0:6c56fb4bc5f0 239 break;
nexpaq 0:6c56fb4bc5f0 240 }
nexpaq 0:6c56fb4bc5f0 241
nexpaq 0:6c56fb4bc5f0 242 p = packet;
nexpaq 0:6c56fb4bc5f0 243 int found = dns_scan_response((const uint8_t **)&p, addr, addr_count);
nexpaq 0:6c56fb4bc5f0 244 if (found) {
nexpaq 0:6c56fb4bc5f0 245 result = found;
nexpaq 0:6c56fb4bc5f0 246 break;
nexpaq 0:6c56fb4bc5f0 247 }
nexpaq 0:6c56fb4bc5f0 248 }
nexpaq 0:6c56fb4bc5f0 249
nexpaq 0:6c56fb4bc5f0 250 // clean up packet
nexpaq 0:6c56fb4bc5f0 251 free(packet);
nexpaq 0:6c56fb4bc5f0 252
nexpaq 0:6c56fb4bc5f0 253 // clean up udp
nexpaq 0:6c56fb4bc5f0 254 err = socket.close();
nexpaq 0:6c56fb4bc5f0 255 if (err) {
nexpaq 0:6c56fb4bc5f0 256 return err;
nexpaq 0:6c56fb4bc5f0 257 }
nexpaq 0:6c56fb4bc5f0 258
nexpaq 0:6c56fb4bc5f0 259 // return result
nexpaq 0:6c56fb4bc5f0 260 return result;
nexpaq 0:6c56fb4bc5f0 261 }
nexpaq 0:6c56fb4bc5f0 262
nexpaq 0:6c56fb4bc5f0 263 // convenience functions for other forms of queries
nexpaq 0:6c56fb4bc5f0 264 extern "C" int nsapi_dns_query_multiple(nsapi_stack_t *stack,
nexpaq 0:6c56fb4bc5f0 265 nsapi_addr_t *addr, unsigned addr_count,
nexpaq 0:6c56fb4bc5f0 266 const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 267 {
nexpaq 0:6c56fb4bc5f0 268 NetworkStack *nstack = nsapi_create_stack(stack);
nexpaq 0:6c56fb4bc5f0 269 return nsapi_dns_query_multiple(nstack, addr, addr_count, host, version);
nexpaq 0:6c56fb4bc5f0 270 }
nexpaq 0:6c56fb4bc5f0 271
nexpaq 0:6c56fb4bc5f0 272 int nsapi_dns_query_multiple(NetworkStack *stack,
nexpaq 0:6c56fb4bc5f0 273 SocketAddress *addresses, unsigned addr_count,
nexpaq 0:6c56fb4bc5f0 274 const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 275 {
nexpaq 0:6c56fb4bc5f0 276 nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
nexpaq 0:6c56fb4bc5f0 277 int result = nsapi_dns_query_multiple(stack, addrs, addr_count, host, version);
nexpaq 0:6c56fb4bc5f0 278
nexpaq 0:6c56fb4bc5f0 279 if (result > 0) {
nexpaq 0:6c56fb4bc5f0 280 for (int i = 0; i < result; i++) {
nexpaq 0:6c56fb4bc5f0 281 addresses[i].set_addr(addrs[i]);
nexpaq 0:6c56fb4bc5f0 282 }
nexpaq 0:6c56fb4bc5f0 283 }
nexpaq 0:6c56fb4bc5f0 284
nexpaq 0:6c56fb4bc5f0 285 delete[] addrs;
nexpaq 0:6c56fb4bc5f0 286 return result;
nexpaq 0:6c56fb4bc5f0 287 }
nexpaq 0:6c56fb4bc5f0 288
nexpaq 0:6c56fb4bc5f0 289 extern "C" int nsapi_dns_query(nsapi_stack_t *stack,
nexpaq 0:6c56fb4bc5f0 290 nsapi_addr_t *addr, const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 291 {
nexpaq 0:6c56fb4bc5f0 292 NetworkStack *nstack = nsapi_create_stack(stack);
nexpaq 0:6c56fb4bc5f0 293 int result = nsapi_dns_query_multiple(nstack, addr, 1, host, version);
nexpaq 0:6c56fb4bc5f0 294 return (result > 0) ? 0 : result;
nexpaq 0:6c56fb4bc5f0 295 }
nexpaq 0:6c56fb4bc5f0 296
nexpaq 0:6c56fb4bc5f0 297 int nsapi_dns_query(NetworkStack *stack,
nexpaq 0:6c56fb4bc5f0 298 SocketAddress *address, const char *host, nsapi_version_t version)
nexpaq 0:6c56fb4bc5f0 299 {
nexpaq 0:6c56fb4bc5f0 300 nsapi_addr_t addr;
nexpaq 0:6c56fb4bc5f0 301 int result = nsapi_dns_query_multiple(stack, &addr, 1, host, version);
nexpaq 0:6c56fb4bc5f0 302 address->set_addr(addr);
nexpaq 0:6c56fb4bc5f0 303 return (result > 0) ? 0 : result;
nexpaq 0:6c56fb4bc5f0 304 }