diff options
Diffstat (limited to 'package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch')
-rw-r--r-- | package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch | 587 |
1 files changed, 0 insertions, 587 deletions
diff --git a/package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch b/package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch deleted file mode 100644 index e1c55f0..0000000 --- a/package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch +++ /dev/null @@ -1,587 +0,0 @@ -From 6b17335209639a56f214d011eaed4ebcde8dd276 Mon Sep 17 00:00:00 2001 -From: Simon Kelley <simon@thekelleys.org.uk> -Date: Tue, 8 May 2018 18:32:14 +0100 -Subject: [PATCH 04/17] Add packet-dump debugging facility. - -Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> ---- - CHANGELOG | 6 ++ - Makefile | 2 +- - bld/Android.mk | 3 +- - man/dnsmasq.8 | 7 ++ - src/config.h | 16 ++++- - src/dnsmasq.c | 16 ++++- - src/dnsmasq.h | 29 +++++++- - src/dump.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - src/forward.c | 37 ++++++++-- - src/option.c | 14 ++++ - 10 files changed, 329 insertions(+), 11 deletions(-) - create mode 100644 src/dump.c - ---- a/CHANGELOG -+++ b/CHANGELOG -@@ -17,6 +17,12 @@ version 2.80 - Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip - are set. Thanks to Daniel Miess for help with this. - -+ Add a facilty to store DNS packets sent/recieved in a -+ pcap-format file for later debugging. The file location -+ is given by the --dumpfile option, and a bitmap controlling -+ which packets should be dumped is given by the --dumpmask -+ option. -+ - - version 2.79 - Fix parsing of CNAME arguments, which are confused by extra spaces. ---- a/Makefile -+++ b/Makefile -@@ -76,7 +76,7 @@ objs = cache.o rfc1035.o util.o option.o - helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ - dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ - domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ -- poll.o rrfilter.o edns0.o arp.o crypto.o -+ poll.o rrfilter.o edns0.o arp.o crypto.o dump.o - - hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ - dns-protocol.h radv-protocol.h ip6addr.h ---- a/bld/Android.mk -+++ b/bld/Android.mk -@@ -10,7 +10,8 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c - dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ - radv.c slaac.c auth.c ipset.c domain.c \ - dnssec.c dnssec-openssl.c blockdata.c tables.c \ -- loop.c inotify.c poll.c rrfilter.c edns0.c arp.c crypto.c -+ loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \ -+ crypto.c dump.c - - LOCAL_MODULE := dnsmasq - ---- a/man/dnsmasq.8 -+++ b/man/dnsmasq.8 -@@ -647,6 +647,13 @@ V4 mapped IPv6 addresses, which have a r - The address range can be of the form - <ip address>,<ip address> or <ip address>/<netmask> in both forms of the option. - .TP -+.B --dumpfile=<path/to/file> -+Specify the location of a pcap-format file which dnsmasq uses to dump copies of network packets for debugging purposes. If the file exists when dnsmasq starts, it is not deleted; new packets are added to the end. -+.TP -+.B --dumpmask=<mask> -+Specify which types of packets should be added to the dumpfile. The argument should be the OR of the bitmasks for each type of packet to be dumped: it can be specified in hex by preceding the number with 0x in the normal way. Each time a packet is written to the dumpfile, dnsmasq logs the packet sequence and the mask -+representing its type. The current types are: 0x0001 - DNS queries from clients 0x0002 DNS replies to clients 0x0004 - DNS queries to upstream 0x0008 - DNS replies from upstream 0x0010 - queries send upstream for DNSSEC validation 0x0020 - replies to queries for DNSSEC validation 0x0040 - replies to client queries which fail DNSSEC validation 0x0080 replies to queries for DNSSEC validation which fail validation. -+.TP - .B --add-mac[=base64|text] - Add the MAC address of the requestor to DNS queries which are - forwarded upstream. This may be used to DNS filtering by the upstream ---- a/src/config.h -+++ b/src/config.h -@@ -117,6 +117,9 @@ HAVE_AUTH - HAVE_DNSSEC - include DNSSEC validator. - -+HAVE_DUMPFILE -+ include code to dump packets to a libpcap-format file for debugging. -+ - HAVE_LOOP - include functionality to probe for and remove DNS forwarding loops. - -@@ -132,6 +135,7 @@ NO_DHCP6 - NO_SCRIPT - NO_LARGEFILE - NO_AUTH -+NO_DUMPFILE - NO_INOTIFY - these are available to explicitly disable compile time options which would - otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or -@@ -164,6 +168,7 @@ RESOLVFILE - #define HAVE_AUTH - #define HAVE_IPSET - #define HAVE_LOOP -+#define HAVE_DUMPFILE - - /* Build options which require external libraries. - -@@ -363,6 +368,10 @@ HAVE_SOCKADDR_SA_LEN - #undef HAVE_LOOP - #endif - -+#ifdef NO_DUMPFILE -+#undef HAVE_DUMPFILE -+#endif -+ - #if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY) - #define HAVE_INOTIFY - #endif -@@ -451,8 +460,11 @@ static char *compile_opts = - #ifndef HAVE_INOTIFY - "no-" - #endif --"inotify"; -- -+"inotify " -+#ifndef HAVE_DUMPFILE -+"no-" -+#endif -+"dumpfile"; - - #endif - ---- a/src/dnsmasq.c -+++ b/src/dnsmasq.c -@@ -366,7 +366,16 @@ int main (int argc, char **argv) - else - daemon->inotifyfd = -1; - #endif -- -+ -+ if (daemon->dump_file) -+#ifdef HAVE_DUMPFILE -+ dump_init(); -+ else -+ daemon->dumpfd = -1; -+#else -+ die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF); -+#endif -+ - if (option_bool(OPT_DBUS)) - #ifdef HAVE_DBUS - { -@@ -1424,6 +1433,11 @@ static void async_event(int pipe, time_t - - if (daemon->runfile) - unlink(daemon->runfile); -+ -+#ifdef HAVE_DUMPFILE -+ if (daemon->dumpfd != -1) -+ close(daemon->dumpfd); -+#endif - - my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM")); - flush_log(); ---- a/src/dnsmasq.h -+++ b/src/dnsmasq.h -@@ -119,6 +119,9 @@ typedef unsigned long long u64; - #include <net/if_arp.h> - #include <netinet/in_systm.h> - #include <netinet/ip.h> -+#ifdef HAVE_IPV6 -+#include <netinet/ip6.h> -+#endif - #include <netinet/ip_icmp.h> - #include <sys/uio.h> - #include <syslog.h> -@@ -598,6 +601,16 @@ struct hostsfile { - unsigned int index; /* matches to cache entries for logging */ - }; - -+/* packet-dump flags */ -+#define DUMP_QUERY 0x0001 -+#define DUMP_REPLY 0x0002 -+#define DUMP_UP_QUERY 0x0004 -+#define DUMP_UP_REPLY 0x0008 -+#define DUMP_SEC_QUERY 0x0010 -+#define DUMP_SEC_REPLY 0x0020 -+#define DUMP_BOGUS 0x0040 -+#define DUMP_SEC_BOGUS 0x0080 -+ - - /* DNSSEC status values. */ - #define STAT_SECURE 1 -@@ -1020,14 +1033,14 @@ extern struct daemon { - unsigned int duid_enterprise, duid_config_len; - unsigned char *duid_config; - char *dbus_name; -+ char *dump_file; -+ int dump_mask; - unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry; - #ifdef OPTION6_PREFIX_CLASS - struct prefix_class *prefix_classes; - #endif - #ifdef HAVE_DNSSEC - struct ds_config *ds; -- int dnssec_no_time_check; -- int back_to_the_future; - char *timestamp_file; - #endif - -@@ -1040,6 +1053,8 @@ extern struct daemon { - char *workspacename; /* ditto */ - char *rr_status; /* flags for individual RRs */ - int rr_status_sz; -+ int dnssec_no_time_check; -+ int back_to_the_future; - #endif - unsigned int local_answer, queries_forwarded, auth_answer; - struct frec *frec_list; -@@ -1094,6 +1109,10 @@ extern struct daemon { - char *addrbuff; - char *addrbuff2; /* only allocated when OPT_EXTRALOG */ - -+#ifdef HAVE_DUMPFILE -+ /* file for packet dumps. */ -+ int dumpfd; -+#endif - } *daemon; - - /* cache.c */ -@@ -1588,3 +1607,9 @@ int check_source(struct dns_header *head - /* arp.c */ - int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now); - int do_arp_script_run(void); -+ -+/* dump.c */ -+#ifdef HAVE_DUMPFILE -+void dump_init(void); -+void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst); -+#endif ---- /dev/null -+++ b/src/dump.c -@@ -0,0 +1,210 @@ -+/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; version 2 dated June, 1991, or -+ (at your option) version 3 dated 29 June, 2007. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see <http://www.gnu.org/licenses/>. -+*/ -+ -+#include "dnsmasq.h" -+ -+#ifdef HAVE_DUMPFILE -+ -+static u32 packet_count; -+ -+/* https://wiki.wireshark.org/Development/LibpcapFileFormat */ -+struct pcap_hdr_s { -+ u32 magic_number; /* magic number */ -+ u16 version_major; /* major version number */ -+ u16 version_minor; /* minor version number */ -+ u32 thiszone; /* GMT to local correction */ -+ u32 sigfigs; /* accuracy of timestamps */ -+ u32 snaplen; /* max length of captured packets, in octets */ -+ u32 network; /* data link type */ -+}; -+ -+struct pcaprec_hdr_s { -+ u32 ts_sec; /* timestamp seconds */ -+ u32 ts_usec; /* timestamp microseconds */ -+ u32 incl_len; /* number of octets of packet saved in file */ -+ u32 orig_len; /* actual length of packet */ -+}; -+ -+ -+void dump_init(void) -+{ -+ struct stat buf; -+ struct pcap_hdr_s header; -+ struct pcaprec_hdr_s pcap_header; -+ -+ packet_count = 0; -+ -+ if (stat(daemon->dump_file, &buf) == -1) -+ { -+ /* doesn't exist, create and add header */ -+ header.magic_number = 0xa1b2c3d4; -+ header.version_major = 2; -+ header.version_minor = 4; -+ header.thiszone = 0; -+ header.sigfigs = 0; -+ header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */ -+ header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */ -+ -+ if (errno != ENOENT || -+ (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 || -+ !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0)) -+ die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE); -+ } -+ else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 || -+ !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1) || -+ header.magic_number != 0xa1b2c3d4) -+ die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE); -+ else -+ { -+ /* count existing records */ -+ while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1)) -+ { -+ lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR); -+ packet_count++; -+ } -+ } -+} -+ -+void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst) -+{ -+ struct ip ip; -+#ifdef HAVE_IPV6 -+ struct ip6_hdr ip6; -+ int family; -+#endif -+ struct udphdr { -+ u16 uh_sport; /* source port */ -+ u16 uh_dport; /* destination port */ -+ u16 uh_ulen; /* udp length */ -+ u16 uh_sum; /* udp checksum */ -+ } udp; -+ struct pcaprec_hdr_s pcap_header; -+ struct timeval time; -+ u32 i, sum; -+ void *iphdr; -+ size_t ipsz; -+ int rc; -+ -+ if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask)) -+ return; -+ -+ /* So wireshark can Id the packet. */ -+ udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT); -+ -+#ifdef HAVE_IPV6 -+ if (src) -+ family = src->sa.sa_family; -+ else -+ family = dst->sa.sa_family; -+ -+ if (family == AF_INET6) -+ { -+ iphdr = &ip6; -+ ipsz = sizeof(ip6); -+ memset(&ip6, 0, sizeof(ip6)); -+ -+ ip6.ip6_vfc = 6 << 4; -+ ip6.ip6_plen = htons(sizeof(struct udphdr) + len); -+ ip6.ip6_nxt = IPPROTO_UDP; -+ ip6.ip6_hops = 64; -+ -+ if (src) -+ { -+ memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ); -+ udp.uh_sport = src->in6.sin6_port; -+ } -+ -+ if (dst) -+ { -+ memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ); -+ udp.uh_dport = dst->in6.sin6_port; -+ } -+ -+ /* start UDP checksum */ -+ for (sum = 0, i = 0; i < IN6ADDRSZ; i++) -+ sum += ((u16 *)&ip6.ip6_src)[i]; -+ } -+ else -+#endif -+ { -+ iphdr = &ip; -+ ipsz = sizeof(ip); -+ memset(&ip, 0, sizeof(ip)); -+ -+ ip.ip_v = IPVERSION; -+ ip.ip_hl = sizeof(struct ip) / 4; -+ ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len); -+ ip.ip_ttl = IPDEFTTL; -+ ip.ip_p = IPPROTO_UDP; -+ -+ if (src) -+ { -+ ip.ip_src = src->in.sin_addr; -+ udp.uh_sport = src->in.sin_port; -+ } -+ -+ if (dst) -+ { -+ ip.ip_dst = dst->in.sin_addr; -+ udp.uh_dport = dst->in.sin_port; -+ } -+ -+ ip.ip_sum = 0; -+ for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) -+ sum += ((u16 *)&ip)[i]; -+ while (sum >> 16) -+ sum = (sum & 0xffff) + (sum >> 16); -+ ip.ip_sum = (sum == 0xffff) ? sum : ~sum; -+ -+ /* start UDP checksum */ -+ sum = ip.ip_src.s_addr & 0xffff; -+ sum += (ip.ip_src.s_addr >> 16) & 0xffff; -+ sum += ip.ip_dst.s_addr & 0xffff; -+ sum += (ip.ip_dst.s_addr >> 16) & 0xffff; -+ } -+ -+ if (len & 1) -+ ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */ -+ -+ udp.uh_sum = 0; -+ udp.uh_ulen = htons(sizeof(struct udphdr) + len); -+ sum += htons(IPPROTO_UDP); -+ sum += htons(sizeof(struct udphdr) + len); -+ for (i = 0; i < sizeof(struct udphdr)/2; i++) -+ sum += ((u16 *)&udp)[i]; -+ for (i = 0; i < (len + 1) / 2; i++) -+ sum += ((u16 *)packet)[i]; -+ while (sum >> 16) -+ sum = (sum & 0xffff) + (sum >> 16); -+ udp.uh_sum = (sum == 0xffff) ? sum : ~sum; -+ -+ rc = gettimeofday(&time, NULL); -+ pcap_header.ts_sec = time.tv_sec; -+ pcap_header.ts_usec = time.tv_usec; -+ pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len; -+ -+ if (rc == -1 || -+ !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) || -+ !read_write(daemon->dumpfd, iphdr, ipsz, 0) || -+ !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) || -+ !read_write(daemon->dumpfd, (void *)packet, len, 0)) -+ my_syslog(LOG_ERR, _("failed to write packet dump")); -+ else -+ my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask); -+ -+} -+ -+#endif ---- a/src/forward.c -+++ b/src/forward.c -@@ -508,6 +508,10 @@ static int forward_query(int udpfd, unio - - if (errno == 0) - { -+#ifdef HAVE_DUMPFILE -+ dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr); -+#endif -+ - /* Keep info in case we want to re-send this packet */ - daemon->srv_save = start; - daemon->packet_len = plen; -@@ -769,7 +773,7 @@ void reply_query(int fd, int family, tim - #endif - - header = (struct dns_header *)daemon->packet; -- -+ - if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR)) - return; - -@@ -796,6 +800,12 @@ void reply_query(int fd, int family, tim - if (!(forward = lookup_frec(ntohs(header->id), hash))) - return; - -+#ifdef HAVE_DUMPFILE -+ dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY, -+ (void *)header, n, &serveraddr, NULL); -+#endif -+ -+ - /* log_query gets called indirectly all over the place, so - pass these in global variables - sorry. */ - daemon->log_display_id = forward->log_id; -@@ -934,6 +944,11 @@ void reply_query(int fd, int family, tim - status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, - !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL); -+#ifdef HAVE_DUMPFILE -+ if (status == STAT_BOGUS) -+ dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, -+ header, (size_t)n, &serveraddr, NULL); -+#endif - } - - /* Can't validate, as we're missing key data. Put this -@@ -1060,6 +1075,11 @@ void reply_query(int fd, int family, tim - setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); - } - #endif -+ -+#ifdef HAVE_DUMPFILE -+ dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr); -+#endif -+ - while (retry_send(sendto(fd, (char *)header, nn, 0, - &server->addr.sa, - sa_len(&server->addr)))); -@@ -1114,8 +1134,8 @@ void reply_query(int fd, int family, tim - bogusanswer = 1; - } - } --#endif -- -+#endif -+ - /* restore CD bit to the value in the query */ - if (forward->flags & FREC_CHECKING_DISABLED) - header->hb4 |= HB4_CD; -@@ -1141,6 +1161,11 @@ void reply_query(int fd, int family, tim - nn = resize_packet(header, nn, NULL, 0); - } - #endif -+ -+#ifdef HAVE_DUMPFILE -+ dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source); -+#endif -+ - send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, - &forward->source, &forward->dest, forward->iface); - } -@@ -1394,7 +1419,11 @@ void receive_query(struct listener *list - pass these in global variables - sorry. */ - daemon->log_display_id = ++daemon->log_id; - daemon->log_source_addr = &source_addr; -- -+ -+#ifdef HAVE_DUMPFILE -+ dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL); -+#endif -+ - if (extract_request(header, (size_t)n, daemon->namebuff, &type)) - { - #ifdef HAVE_AUTH ---- a/src/option.c -+++ b/src/option.c -@@ -161,6 +161,8 @@ struct myoption { - #define LOPT_TFTP_MTU 349 - #define LOPT_REPLY_DELAY 350 - #define LOPT_RAPID_COMMIT 351 -+#define LOPT_DUMPFILE 352 -+#define LOPT_DUMPMASK 353 - - #ifdef HAVE_GETOPT_LONG - static const struct option opts[] = -@@ -327,6 +329,8 @@ static const struct myoption opts[] = - { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL }, - { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY }, - { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT }, -+ { "dumpfile", 1, 0, LOPT_DUMPFILE }, -+ { "dumpmask", 1, 0, LOPT_DUMPMASK }, - { NULL, 0, 0, 0 } - }; - -@@ -500,6 +504,8 @@ static struct { - { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, - { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, - { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, -+ { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL }, -+ { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL }, - { 0, 0, NULL, NULL, NULL } - }; - -@@ -1811,6 +1817,14 @@ static int one_opt(int option, char *arg - ret_err(_("bad MX target")); - break; - -+ case LOPT_DUMPFILE: /* --dumpfile */ -+ daemon->dump_file = opt_string_alloc(arg); -+ break; -+ -+ case LOPT_DUMPMASK: /* --dumpmask */ -+ daemon->dump_mask = strtol(arg, NULL, 0); -+ break; -+ - #ifdef HAVE_DHCP - case 'l': /* --dhcp-leasefile */ - daemon->lease_file = opt_string_alloc(arg); |