Skip to content

AmiNetXDuo 0.25.4 (NetX Duo 6.5.1, ThreadX 6.5.1)

Choose a tag to compare

@tinic tinic released this 25 Aug 23:56
· 148 commits to main since this release

AmiNetXDuo 0.25.4 (NetX Duo 6.5.1, ThreadX 6.5.1)

Drop AmiNetXDuo-0.25.4.lha on a Workbench window and run Install-AmiNetXDuo.

sha256  abc79985af378cb59bf0ef778b3f995b69993f57027d99681065360afdf34ef6  AmiNetXDuo-0.25.4.lha
  • Four interfaces can be online at once, up from two. An A1200 with a PiStorm32 has two conventional cards -- a 3c589 in the PCMCIA slot, and room for another -- and two more the accelerator supplies, genet.device for the Pi's gigabit Ethernet and wifipi.device for its WiFi, so four is what that machine has. It costs 4,872 bytes of resident memory on every machine, whether it brings up one interface or four -- 4,032 of the single allocation the stack makes, 808 of interface-list floor and 32 of bsdsocket.library's BSS -- which a 2 MB A1200 with no Fast RAM was measured carrying traffic with. A machine that would rather have the bytes builds fewer slots with cmake -DAMINETXDUO_MAX_INTERFACES=2
  • An AddNetInterface that fails puts back the interface it displaced. On a machine with every slot in use, bringing up an interface takes a slot from one the boot started on its own -- and if the new interface then fails after its device has opened, which is what a copied interface file with an address the machine already has does, the one that stood down was left defined and not attached, with nothing said about it. It now comes back up, with its address, its DHCP and its routes
  • An interface whose GATEWAY cannot be reached still comes up. A next hop on no network this machine is on is refused by the stack, and AddNetInterface treated that as a reason to destroy the interface -- which on a full machine also cost the interface that had stood down to make room for it, so one mistyped line in one file took the network off a working machine. The interface comes up, and the command says the route was refused and which line of which file to look at
  • NetShutdown takes the machine off the network, and a second one can put it back. Three separate faults, each of which left something running after the command reported success. The card was told the stack's belief about the wire rather than the fact it had been sent an offline command, so one out-of-service read stopped S2_OFFLINE ever reaching that interface again -- and a2065.device 2.16 ignores AbortIO(), so the reads already posted stayed with the device, the reader timed out reaping them and the close was refused while the card went on receiving. The interface count was then written to zero over the top of whatever the close loop had actually managed, so everything that walks the stack -- the capture taps, the mDNS and DHCPv6 loops -- walked slots that were still occupied. And the TCP: handler was never asked to go down, so the first machine to open TCP: kept a Process, its DOS entry and 371,648 bytes for the rest of that boot, with bsdsocket.library unable to expunge
  • A receive window too small to be useful is advertised as zero rather than as a number the sender cannot use. RFC 1122's silly-window-syndrome rule: below one maximum segment the window goes on the wire as zero, so the sender waits for a real opening instead of dribbling undersized segments at a receiver that has no room for them
  • 64-bit arithmetic is faster, and four shipped programs stopped carrying a second copy of it. iperf, httpd, Profile and profspin were resolving __udivdi3 out of libgcc rather than this tree's own, because GCC invents that call late in link-time optimisation, after our archive has already been scanned. The helper that now serves them is 4.8x libgcc's on the common path, and the wide-divisor branch -- a 64-bit divisor, which was a 64-iteration shift-and-subtract loop -- goes from 847 to 57 microseconds an operation on a 68020. The flag that used to trim the unused helpers charged every program that linked them 0.8 to 6.6 per cent of its whole size for it, and no longer does
  • A machine with no Fast RAM gets a packet pool it can actually receive into. The pool was a sixteenth of free memory, which on a 2 MB A1200 with 1.2 MB free buys 47 packets, and the receive window derived from it was then overridden by an 8 KB floor -- so the socket advertised a window larger than the pool that had to hold it. Measured on an A2065 bridged to a real sender for 15 seconds: zero-window events fall from 113 to 6 and throughput rises 10.9 per cent
  • getsockopt(SO_RCVBUF) on a UDP socket answers the datagram queue rather than a TCP window. It returned 8192 whatever the queue actually was, which is a TCP number on a socket that has no TCP in it
  • A 3c589 under load loses fewer frames, and the counters say which loss it was. The receive FIFO's head packet was held for the whole of the call up into the stack, occupying receive memory that arriving frames needed; it is released after the last read from the FIFO now. Transmit underruns were also being added to the overrun counter, which SANA-II reports as "Receive ring overruns", so a card with a transmit problem read as a card that was being flooded. They have their own counter
  • The libraries build for 68020 and 68060 again. The check that refuses a program whose PC-relative branches cannot reach was reading local symbols out of an Amiga load file, which has none, so it refused every build of every target for those processors -- the shipping libraries among them
  • Three commands answer correctly where they used to answer plausibly. The browser console's first SetMode() or WaitForChar() of a session was refused with an invalid-lock error, because neither the break port nor the shell task is known until after the first packet. CheckNetConfig's four IPv4 checks -- no default route, an off-network router, no name server, an off-network name server -- were dead on every configuration an IPv6 build parses, which is all of them. And ConfigureNetInterface told a user with a working DHCP client that the machine had none, because the client being mid-allocation answers EBUSY and only one of the four possible errors was being read

0.25.3

  • DEVS:NetInterfaces may hold as many interface definitions as you put in it. The reader stopped at two: the third and later files were discarded as they were read, in whatever order the filesystem handed them over, so which two survived was not something the drawer told you -- and nothing said a word about it, so CheckNetConfig went on reporting the card and the driver healthy for a machine whose configuration it had only half read. The list has no ceiling now. The one that remains is the hardware's -- two interfaces attach at a time -- and it is a refusal at the moment you ask, naming the interfaces that are holding the slots
  • The interface you name is the interface that comes up, whichever file it is in DEVS:NetInterfaces. Two interfaces can be online at once and a drawer may describe more than two; the boot brought up whichever came first alphabetically and took both slots, so AddNetInterface wifi0 on a machine that also had eth0 and eth1 was refused "no space left on device" with nothing else in use, and the card the machine was allowed to use was decided by the alphabet. An interface the boot started on its own now gives its slot to one you ask for by name, and says which one it was and how to bring it back. An interface you named keeps its slot: a third AddNetInterface is still refused, with the names of the two that are holding them
  • An interface reports its own name. When an earlier interface file named a card that is not in the machine, the machine came up on the next card with the next card's address and reported it under the missing card's name -- in netstat -i and in ShowNetStatus, which also said the interface file had been changed after the network started, about a file nobody had touched -- while the interface that was actually running was listed as offline
  • A mistyped DEVICE= costs a message and nothing else. Bringing up an interface whose driver is not on the machine never takes down one that is working
  • A keyword this stack reads and ignores on purpose is no longer filed as a problem with your file. An interface file written for Roadshow carries IPREQUESTS, WRITEREQUESTS, COPYMODE and MULTICAST, which are accepted so that a stock file loads here unchanged and are then acted on by nothing -- and every command that loaded the configuration printed six lines about each of them under the heading "Problems in the configuration:", ending with the sentence "The line is harmless and can stay". One machine's netstat -i was thirty-three lines, twenty-one of them that lecture, before the table, and ShowNetStatus DHCP answered with the same essay and then said there is no interface called DHCP. Ordinary commands now say nothing about them; CheckNetConfig, whose job is auditing the file, prints every one with its line and its reason under a heading of its own, and a file whose only findings are those is told it has nothing wrong with it. A real fault is still printed by every command, with the file and the line -- and by AddNetInterface once rather than five times, which is how often it reads the file
  • netstat -i names an interface that is defined and did not attach. The table is built from the live stack, so on a machine describing more interfaces than there are slots the ones that did not come up appeared in no column of that command at all; there is a line under the table for them now, the same fact ShowNetStatus reports as the defined state
  • A bring-up that fails says which call failed and what it answered, rather than describing the trouble in prose and then sending you to a debug log. That log is AMINETXDUO_LOG, it is off in every build that ships, and a machine that has just refused to put its network up is the worst possible moment to be told to go and read something it cannot write
  • The waits inside anxnet.device measure time rather than counting instructions. Every one of them was a spin over a bus read whose count had been calibrated on a 14 MHz 68020 -- including the three hundred milliseconds a PCMCIA card is held in reset, which the card's own documentation puts at one to two hundred as a minimum -- so on an accelerated machine each of them finished early, and by as much as the processor was faster. They are clocked against the raster beam now, which runs off the chipset oscillator and keeps the same time on every Amiga, and each one is floored by the old count as well, so no machine waits less than it did before. This was found by reading the code while looking into an accelerator report, and it is not established to have caused that report or any other: there is no accelerated machine here, and the new behaviour is unverified on physical hardware. CheckNetDevice prints the spins it measures per raster line, which is the number that says whether a machine was affected -- tens on a stock A1200, tens of thousands on an accelerator
  • IGMP receive checksums are checked again. A guard that should read "unless the interface and the packet BOTH say the checksum has already been done" was negated the wrong way round in the vendored NetX Duo on 2026-08-06, and every build that ships has interface capabilities compiled in, so since that day an IGMP membership message has been accepted without its checksum being looked at at all
  • A transfer into this machine no longer stops dead for a third of a second at a time. Two faults in the vendored TCP, both measured on the A1200 against a Linux sender. A window that fell below one segment could only be reopened from the receive path, which a program parked in recv() never reaches, so the sender sat in persist backoff long after the application had made room; and the sender's window probe -- one byte past the advertised edge -- was trimmed away to nothing and then matched none of the conditions that produce an acknowledgment, so it went unanswered and the probe interval doubled, and doubled again. The window is now reopened from the delayed-acknowledgment timer once two full segments fit, and a segment trimmed to nothing is answered with the current sequence and window, which is what RFC 1122 asks for. About one transfer in ten used to lose five to ten per cent this way and the occasional one collapsed outright
  • Multicast name service no longer takes the machine off the air for half a second at a time. The responder drained its whole receive queue in a single pass with its mutex held, and every record in every packet is compared against the entire peer cache: on a 14 MHz 68020 with a populated cache that measured one hundred to five hundred milliseconds, during which the IP thread never ran and no acknowledgment left the machine, and one transfer in six crossed a burst of LAN chatter and lost five to ten per cent to the dead air. The thread hands the processor back between resource records now, not merely between packets -- one multi-record response on its own was holding the machine one hundred and thirty milliseconds
  • A certificate chain signed with RSASSA-PSS verifies. It was not failing verification, it was failing to parse: id-RSASSA-PSS is the one signature algorithm in X.509 whose parameters are not NULL, the parser refused the SEQUENCE they arrive in and answered that the certificate was invalid, so any chain with a PSS link anywhere in it was simply unreachable
  • A 3c589 in the PCMCIA slot keeps its own hardware address. Read straight after the attach-time reset the card's EEPROM answers with words that have not settled, and they look enough like an address to pass every check there was, so the machine took a different made-up address on each boot -- measured on the wire as 54:00:72:65:1b:03 one boot and 40:4c:00:10:09:db the next, where the card's real address is 00:10:4b:df:09:db. A new address every boot is a new DHCP identity every boot. The address is now read only once the part says it has settled, every word is read twice and believed only when both reads agree, and a card whose EEPROM never answers gets the same stable derived address the driver already gives a card with no EEPROM at all
  • A PCMCIA card that comes up deaf is reset and tried again instead of needing the machine rebooted. Whether a 3c589's receiver hears the wire is decided at power-up, on an A1200 the slot is never reset properly at power-on -- Commodore's unfixed bug, and 3Com cards are the documented sufferers -- and no register a driver can write changes it. The claim now holds the slot in reset for the three hundred milliseconds the card's documentation asks for, writes the activate bit that nothing on an Amiga has ever written, and if the unit is online and has transmitted and heard nothing back two seconds later, halts the chip and tries again until the first frame arrives. CheckNetDevice counts the retries
  • A card whose interrupt line has stopped delivering keeps receiving. Transmit needs no interrupt, so a machine in this state looks online and is deaf; the driver counts the interrupts its servers actually claim, and while none have arrived -- or after ten vertical blanks with traffic outstanding and none claimed -- it services the chip from the blank it is already on. A network at fifty frames a second where there was none, and the first genuine interrupt hands the work back
  • httpd answers on IPv6 as well as IPv4. One wildcard listener now carries both families, because NetX keys its listen table by port alone and a second socket on [::] cannot be opened beside the first: the earlier attempt at two listeners answered the IPv6 connection from the IPv4 socket and then reset it. Naming an ADDRESS still picks that address's family, which is what naming one asks for
  • httpd <drawer> -C starts from S:User-Startup, which is the machine that most wants it. Intuition's screen list is empty until something opens a screen, and LoadWB is at the end of the startup sequence, so the check that exists to make a machine which cannot serve a screen refuse at once rather than answer 503 to the first person who asks was reading "not yet" as "no" -- and it took the whole server down with it, on exactly the headless machine being brought up over the network. An empty screen list is a state now, not a verdict: the server starts, says once that there is no screen, and the console begins working by itself when Workbench opens, with nobody restarting anything. A machine that can never serve a screen -- graphics or intuition below V39 -- still refuses, and so does a screen that is in front and cannot be read
  • The installer offers to install anxnet.device. The archive has carried the driver in Devs/Networks/ for as long as it has existed and the installer left it sitting there for a user to copy across by hand. That did not make an upgrade safer, it made the driver unmaintainable: every reinstall put the rest of the software back and left whatever driver was already on the disk exactly where it was, so a fixed driver and a stale one were indistinguishable from outside the machine -- which cost an evening on a real A1200, several build verdicts recorded against the wrong binary, and a bug that had already been fixed reading as a fix that did not work. It is a page of its own now, defaulting to yes, and copying the file changes nothing by itself: which driver a machine opens is the DEVICE= line, and the installer still writes that with the driver that came with the card. An existing DEVS:Networks/anxnet.device is renamed to anxnet.device.old exactly the way the libraries are, and a driver missing from the archive is refused before anything on the disk is touched
  • The Examples and Terminal drawers the installer creates have icons, so Workbench draws them. A drawer's icon is a sibling of the drawer and not a file inside it, and every copy in the installer that handles a drawer copies that drawer's contents -- so Examples.info and Terminal.info were packed in every archive and copied out of none of them, and both drawers arrived on the disk holding the right files and invisible to anybody who did not open a Shell. What let them ship that way was that nothing compared the two lists that describe a release, what the packer packs against what the installer copies; something does now, and it refuses to build an archive holding a file nobody has decided about

0.25.2

  • AmiTCP: is assigned, so a program that opens AmiTCP:libs/usergroup.library as a file finds one. 0.25.1 made ixemul clients work by holding usergroup.library open, which satisfies a path-qualified OpenLibrary because Exec falls back to matching the file part; that does nothing for a program which Locks or Opens the path instead. The installer makes the assign and writes it into S:User-Startup, and the library makes it on first open if nothing else owns the name -- so a hand-installed library works too. A machine with a real AmiTCP on it keeps its own
  • Every shipped binary is smaller. bsdsocket.library 395,328 to 352,960, tls.library 242,312 to 188,312, usergroup.library 7,684 to 7,172, and anxnet.device 33,228 to 30,540. Nothing about the code changed; the toolchain can now do link-time optimisation, and anxnet.device was additionally being built -O2 for the whole image where the rest of the tree is -Os -- measured on the A1200 with an A2065, that cost 21 KB for no read-throughput difference that survives interleaving
  • The network is faster on the same hardware. An A1200 with an A2065 reads 595 KB/s where it read 543, and writes 331 KB/s where it wrote 321. Nothing about the stack changed; the toolchain can now do link-time optimisation, and the whole-program view is worth about ten per cent on the receive path. Measured interleaved over twenty-six runs, thirteen per arm, with the two distributions not overlapping at all
  • Building from source needs the toolchain that tools/fetch-toolchain.sh installs, and refuses an older one rather than producing something subtly wrong. The previous pin cannot do link-time optimisation at all -- its ld answers -plugin PLUGIN (ignored) -- and its own gcc revision no longer exists upstream, so tools/build-toolchain.sh could not fetch it either

0.25.1

  • Programs built against ixemul.library work. wget and every other GeekGadgets client reaches the network through ixnet.library, which opens bsdsocket.library and then looks for usergroup.library; not finding one, it closed the stack it had just opened and reported no network at all, so every socket call in that program answered "Function not implemented" while the interface was up and ping worked. The library now holds usergroup.library open for as long as it is loaded, which is what the other stacks do

0.25.0

  • A DHCPv6 lease is renewed once, when it is due, rather than rebound twenty-five times a second forever. A server that answers with T1 and T2 of zero is telling the client to pick its own renewal times; the client read zero as "renew now" and re-entered the rebind state on every pass, which also meant a renewal never actually reached the wire and the address's lifetime shrank each time. On the lab's network that was 2544 rebinds in five minutes, and is now none

  • The remote desktop shows the screen and not the memory beside it. A graphics card rounds a screen's bitmap up to the board's own pitch, and the console took its width from the bitmap, so a 1368-pixel-wide screen was served as 1600: two hundred and thirty-two columns of whatever the card had off the right-hand edge, on every frame, and seventeen per cent more data to send than the screen contains. A planar screen had the same, rounded up to sixteen

  • An encrypted connection is three to five times faster and no longer takes the machine off the network while it runs. A 4096-bit certificate -- which is what every Let's Encrypt chain ends at -- was handed to the reference arithmetic instead of the 68k routines, one silent block of about ninety seconds per handshake, and the routines that did run gave the rest of the machine a turn only seventeen times in an operation. fetch https://www.gnu.org/ goes from 100 seconds to twenty or thirty, and a machine that used to stop answering for its own address for a minute at a time now answers throughout

  • NetSetup no longer writes uninitialised memory into the interface file. A call that did not pass IPV6= emitted a CONFIGURE6 line holding whatever was on the stack, into a file the stack reads on the next boot

  • A DHCP server's domain is accepted in the forms servers actually send it: an underscore, which a great many consumer routers use, and the fully qualified form ending in a dot, which is stored without it. Refusing either left the machine with no default domain and no search suffix at all, because both come from that one option

  • A malformed DHCPv6 option no longer costs the client the configuration it already had. The DNS servers and the search list are now replaced only after the whole message has parsed, so one bad option in a Reply cannot leave the machine with neither

  • A DHCPv6 exchange that fails on a new network no longer republishes the previous network's DNS servers, and a setup that fails once no longer disables DHCPv6 for the life of the machine

  • A DHCP option that could not be read asks to be read again on the next pass, for the host name, the domain and the search list as well as the servers. One that can never succeed keeps what it has and says so once, rather than retrying forever

  • A router's advertised DNS servers and search domains expire when their lifetime runs out, and belong to the interface and the source that advertised them, so one card's advertisement no longer withdraws another's

  • Multicast name service clears its cache when asked, keeps a browse alive while more than one program is using it, retries a publication that failed, and gets its goodbye onto the wire at shutdown. A lookup across two cards no longer spends the whole timeout on the first

  • A second network card that cannot attach is dropped rather than taking the whole stack down, and AddNetInterface keeps a card that could not get a link-local address instead of removing it

  • telnet to a name whose first address does not answer no longer takes three minutes. The retry now draws from the timeout the caller asked for rather than starting a fresh one

  • nslookup reports a record it cannot read and carries on, rather than abandoning the answer

  • httpd serves a drawer containing a name too long to list, minus that entry, instead of dropping the connection for every client that asks

  • The WebSocket console refuses malformed control frames, oversized control words and invalid UTF-8, and keeps pipelined input under backpressure

  • getnameinfo() no longer fails when the port has no name in DEVS:Internet/services. The flag that requires a name is about the host, and the peer's port on an accepted connection never has one

  • The release archive contains C:ssh again. The packer only copied a client somebody else had built, so an archive made any other way was simply missing it

  • A connected UDP socket becomes readable again. The readability test and FIONREAD read the source port from a packet still on the receive queue, where the UDP header has not yet been stripped, so the port never matched the peer and the socket looked permanently empty; sntp timed out on every attempt and iperf in UDP mode burned each slice

  • FIONREAD on a datagram socket reports the payload without its UDP header, and agrees with what a peeking read returns

  • A UDP socket reports an ICMP error as readable and clears it through SO_ERROR, instead of leaving a program waiting for a datagram that will never arrive

  • A datagram socket honours shutdown(): a socket shut down for reading reports no bytes, and the disconnected state is consistent between two tasks looking at it

  • A UDP or raw socket only accepts what its bind and its connect name. Datagrams from other peers, and to other local addresses, were delivered to a socket that had asked for one endpoint

  • An IPv6 link-local address keeps its zone through bind, connect, accept and receive, and a local zone is no longer confused with the peer's. An address on a machine with two cards named whichever interface the stack looked at first

  • IPv6 multicast honours the scope zone of the address, and a UDP disconnect clears the zone it was using

  • The path MTU is measured on the interface actually selected for the destination, not the first one

  • getnameinfo() with NI_NAMEREQD returns the numeric port when the service has no name. The flag is about the host half -- the peer port on an accepted connection is ephemeral and is never in DEVS:Internet/services, so the whole call failed and the caller lost the host name that had in fact resolved

  • Undefined flags to getnameinfo() are refused rather than ignored

  • A TCP socket reports its queue depths correctly. The receive walk followed a field that holds a sentinel on a ready packet -- an odd address, so a NetStatus query on a socket with two or more queued segments took an address error -- and Send-Q counted headers as application data

  • Encrypted connections work above descriptor 255, no longer race two tasks creating a session at once, and honour nfds when waiting on buffered data

  • A resumed encrypted session is bound to the parameters that were cached with it, so it cannot come back with different ones

  • The router-advertised DNS servers and search domains expire when their lifetime runs out, and are owned per interface and per source, so one card's advertisement no longer withdraws another's

  • A DHCP lease's DNS servers, host name, default domain and search suffix are reconciled against what the lease actually offers, and a server withdrawn from a renewed lease is withdrawn from the resolver

  • A statically configured DNS server survives a lease withdrawal

  • Multicast service discovery retries a publication that failed, reports every publication failure rather than the first, keeps a shared browse alive while more than one program is using it, and gets its goodbye onto the wire at shutdown

  • A multicast name lookup across two cards no longer spends the whole timeout on the first one

  • httpd conditional requests compare entity tags exactly, distinguish weak from strong, and keep a complete tag list rather than the last entry

  • httpd parses byte ranges without guessing, and refuses a malformed request version, a malformed header field name, an unsupported expectation, an undelimited transfer coding and an invalid WebDAV Overwrite directive

  • httpd keeps the old file until its replacement is complete, never truncates an existing file to hold a temporary, and reports a failure on the final write instead of returning success

  • httpd refuses a MOVE or COPY whose destination is inside its source, never deletes the source after a partial copy, and refuses a name it would have to truncate anywhere in the tree

  • httpd aborts a directory listing when the scan fails, rather than serving the part it managed to read as though it were complete

  • httpd matches a WebSocket negotiation token exactly and requires an exact takeover query

  • The WebSocket console rejects a non-minimal frame length, a prohibited or malformed close code, invalid UTF-8 in a text message or close reason, and enforces the message ceiling across a fragmented message

  • The WebSocket console keeps pipelined input under backpressure, evicts only at a frame boundary, preserves a queued close over a ping, and stops polling an input socket that is blocked

  • telnet, whois and nettrace finish a write that the stack accepted only in part, instead of sending a truncated request

  • telnet renders a bare carriage return as the NVT defines it

  • nslookup confines a name to its own RDATA field, so a malformed record cannot read past it

  • tftp removes a partial download when the local write fails, and accepts the transfer port only from the server it asked

  • nc and iperf honour IPv6 for a wildcard listener and in server mode; iperf ignores a truncated UDP datagram and pins a UDP run to its first peer

  • nc times out a UDP listener that never receives a first datagram

  • Every command-line tool refuses a negative or overflowing numeric argument -- timeouts, units, ports, scope indices, port ranges, capture geometry and workloads -- rather than narrowing it into something else

  • A service entry without a protocol, an oversized protocol number and an oversized service port are refused by the name database

  • SIOCGIFCONF reports the bytes written when given a buffer, so a caller cannot walk past the end of a short one

  • Socket option buffers that are not aligned are tolerated rather than read as though they were

  • A socket timeout of a full day no longer wraps, and a finite wait is charged the time it has already spent

  • A raw or packet-filter channel is safe to use from two tasks at once: interface replacement is transactional, and property, readiness, sizing and signal-mask queries are taken under the lock

  • The SANA-II layer bounds a receive to the data actually copied, and reaps writes after the final drain

  • The AddNetInterface and ConfigureNetInterface tools honour a hardware address and an initialisation delay supplied at run time

  • make-dist.sh builds what is missing instead of describing the commands and stopping

  • A certificate that is not allowed to be used the way the connection needs is refused. A leaf's keyUsage was never checked, so a certificate marked for encryption only, or for signing only, was accepted for either; a program is now told which of those it hit rather than being told the handshake failed

  • DHCP runs on the interfaces that asked for it. The client enrolled interface 0 whichever way it was configured, so a machine with a fixed address on its first card and DHCP on its second leased for the wrong one

  • An interface that goes down and comes back gets its address again. Neither the DHCPv6 client nor the router solicitation was restarted, so the machine kept whatever it had before the cable moved, or nothing

  • Multicast name service survives being switched off and on. Disabling an interface tested whether it was enabled outside the lock that protects the answer, enabling one that then failed left records published for a service that was not running, and deleting the responder left it in its multicast groups and left callbacks pointing into freed memory

  • A network service that fails to start no longer leaves its worker running. Telnet, TFTP, MQTT, the cloud client, both DHCP servers and the mDNS responder each created threads and timers and then returned an error without stopping them, so a failed start left the machine slower and the second attempt fighting the first

  • Browsing for services stops reading at the end of the caller's array. It kept comparing against entries past the end when there were more services than room for them

  • A DHCPv6 packet whose first option is one we do not know is handled the same way every time. The unknown option left the result of the previous option's parse standing, which was whatever the compiler had put there

  • PCMCIA network interrupts now run through card.resource's status-change callback, including the V39 post-status phase and the Kickstart 2.x Gayle acknowledgement, instead of registering a raw PORTS server that bypassed the socket owner

  • Removing a PCMCIA card takes the unit offline without touching the empty socket, releases ownership from task context, and keeps the handle ready for a validated reinsertion. Reinsertion reruns CIS, COR and chip attachment and restores an online unit unless the caller explicitly put it offline

  • A PCMCIA card configured for use is reset before its handle is released, and an empty or initially busy socket can be claimed on a later OpenDevice() instead of requiring the device resident to be reloaded

  • cnet16 detection repeats the complete reset-port pulse after switching odd-register reads to the 16-bit path. The old retry changed the ISR read width only, after the reset read that needed the same workaround had already failed

  • Creating or resetting a ThreadX thread fails cleanly when Exec cannot allocate its native Task. The failed Task creation was invisible to ThreadX, which published and could start a thread that had nothing underneath it

  • Thread teardown still detaches the native Task and recovers the scheduler baton when the reaper has no spare signal bit. That fallback returned before recording the live zombie, so the caller could free its stack and the scheduler could remain owned by a thread that no longer existed

  • Thread stacks are rejected whenever their ranges overlap, including when the new range wholly contains an existing one. That containment passed the endpoint-only check and let two Tasks use the same memory as a stack

  • A TLS host name too long for both certificate verification and SNI is refused instead of silently shortened. Truncating the identity could verify or contact a different host from the one the program requested

  • A transmit request that finds the hardware ring full after copying is now marked asynchronous before it is requeued. It still carried IOF_QUICK, so its eventual completion was not replied and the caller could wait forever

  • The mbuf cluster limit remains a limit when two tasks grow the pool together. Both could observe the last slot free while allocation ran outside the lock and each add a cluster

  • httpd refuses a document root longer than it can hold instead of serving a shortened one, which named a different directory

  • The 68020 modular-squaring primitive is a no-op on an empty modulus rather than running its loop backwards through memory. Nothing in the stack calls it that way; a program using the routine directly had no way to know that was required

  • Asking to share the library base no longer loses the rest of the request. The answer is still no -- signals and timer state belong to the task that opened the library -- but refusing the tag discarded every tag after it in the same call, so a program that asked to share and linked its errno in one go ran without an errno for its whole life

  • Leaving a range of multicast addresses removes the ones that are there. A program that joined them one at a time, or that had already left one, was told the whole request failed and stayed in every group in the range

  • getaddrinfo() with no hints resolves a service that exists only over UDP again -- tftp, ntp, syslog and the rest. Tightening the protocol checks moved the lookup to after the socket type was defaulted, and the "try both protocols" step could then never run

  • A program killed while it was inside the stack no longer stops the network for everything else. Its claim on the stack could not be given back by a task that no longer exists, and nothing else was allowed to give it back either

  • Two programs closing encrypted connections at the same time no longer corrupt the list the encryption library keeps of its open sessions

  • Shutting the stack down no longer frees memory a stuck driver request can still reach. When a device refuses to return a request, the interface is deliberately left behind, and the packet pool it points into was deleted anyway

  • Asking for another task's credentials returns a copy. The pointer named that task's own context, which could close the moment the search released the scheduler, so the caller read it after it was gone

  • A TCP: session no longer looks at its socket after closing it. If another holder closed at the same moment, that close was the last one and the memory was already free

  • An unbound interface only releases a packet for commands that carry one. A control command's packet field is whatever was there before, and it was being freed as though it were a packet

  • A LANCE card keeps sending across a multicast filter change. Changing the filter reinitialises the chip, which threw away frames it had already accepted and reported as sent

  • Two interfaces can no longer be bound to the same slot. The search stopped at the first free entry and rebound it even when the interface already held one further along

  • An interface that survives a failed device open keeps its configuration. The map from device to configuration kept naming the pre-compaction entry, so a later lookup lost a working interface

  • S2_ONEVENT with an empty mask is refused instead of waiting forever, since no event can share a bit with it

  • A failed name-server query replies as the request it was made with. The reply went out through the SANA helper, which writes a byte count into the field an IOStdReq uses for something else

  • A write that raced going offline is refused rather than queued onto a stopped unit, where nothing was left to send it

  • Waiting for online or offline no longer misses a transition that lands between the state test and the queue

  • Capture channels survive being closed and reopened while another call is in flight. A read, a close, an ioctl or an arriving packet could all act on the replacement channel that took the same number

  • The random pool credits entropy without wrapping. A large credit through the public interface could carry a partly seeded pool back toward empty

  • A multicast range no longer wraps its reference count

  • The library refuses an unsolicited TLS renegotiation. There is no renegotiation API here and one connection is one handshake, but the vendored stack would accept a server's request to start a second one without asking

  • A reader that outlives its removal no longer has its stack freed underneath it. The teardown read a gauge of how many such tasks were alive, and that number can be unchanged across the window if an older one exits as this one is created, so the check passed and the memory went back while the task was still standing on it

  • A LANCE card that reports a buffer error or an underrun starts sending again. Both clear the chip's transmit enable, so retiring the frame left a card that says it is running and never transmits another packet until the machine is rebooted

  • The transmit watchdog resets a card that is stuck, not one that is busy. It fired whenever the ring stayed full for the interval, which under a sustained transfer is a card that is keeping up, and the reset dropped what was queued

  • Adding or removing a multicast address is one transaction. The exact list and the hardware filter built from it were updated separately, so a program doing this from one task while another received could leave the card filtering on a list that no longer matched

  • Configuring an interface by name keeps hold of it for the whole call. The name was turned into a slot number once, and the slot could be freed and reused underneath, so the rest of the call could configure a different interface. Removing an interface now waits for such a call rather than pulling the slot out from under it

  • An address allocation started with BeginInterfaceConfig() survives the program that asked for it closing the library. The worker outlives the caller by design, and could be left polling a slot on a stack that had been taken down and rebuilt behind it

  • Removing an interface by name removes the one that was named. The name was resolved to a slot, the lock was dropped, and the slot was reused, so a program could take down a different interface than it asked for

  • The resolver's configuration is read and written one whole change at a time. A program reading the name server list or the domain name while another set them could see a half-written table or a torn string

  • A socket option passed by reference with a null pointer is refused. The pointer was written through, and on a machine with no memory protection that write lands on the interrupt vectors at address zero

  • recvfrom() refuses a source-address buffer that arrives without its length, before it takes a datagram off the queue rather than after

  • recvmsg() reports the size of the source address when the buffer given for it is too small, instead of leaving the caller with nothing

  • getaddrinfo() refuses hints whose output fields are not clear, which is how a reused or partly initialised structure shows itself

  • getnameinfo() appends the zone to a link-local address only when asked for it with NI_WITHSCOPEID

  • An event or an error that arrives while a program is reading them is no longer lost. Reading the pending error, the urgent-data mark and the event set each took two steps, and the network task posts between them, so a new event could be erased by the write that cleared the old one

  • Dup2Socket() reserves a descriptor. Asking for one without a socket did nothing, so the descriptor a program was holding for later could be handed to the next socket it opened

  • getaddrinfo() fails cleanly when it runs out of memory. A failed allocation was added to the list anyway, so the call could report success with a list shorter than the answer, or empty

  • getaddrinfo() checks the protocol it is given. ai_protocol was never looked at, so a request for UDP with no socket type was answered with the TCP service, and a contradictory pair such as stream over UDP was accepted

  • The source address in a message's own control data decides where a multicast datagram leaves from, ahead of the standing interface option

  • WaitSelect() reports a stack that went away while it was waiting, instead of returning as though the wait had simply timed out

  • SBTC_CAN_SHARE_LIBRARY_BASES answers FALSE and refuses a request to share, because signal masks and timer state belong to the task that opened the library. Asking is refused; passing FALSE is not, so a program setting several tags at once does not lose the ones after it

  • The library is not unloaded while a ThreadX Task can still be running code inside it. Stopping the kernel can refuse, or time out once stopping has begun, and the refusal was logged and then ignored: the segment went back to the operating system anyway, with tasks still standing on it

  • connect() stops trying when it gives up. On a timeout or a break it returned the failure and left the SYN retransmission running, so a reply arriving afterwards could connect a socket whose caller had already been told the connection failed

  • accept() no longer answers a handshake twice. A wait that expired inside it wound the connection back to listening, and the next call sent a second SYN and acknowledgment for a connection already half open, with different sequence numbers

  • MSG_DONTWAIT works. It was accepted and discarded, so a program asking for one operation not to wait waited anyway

  • A large SO_RCVTIMEO or SO_SNDTIMEO is the timeout that was asked for. The conversion to ticks overflowed, so a long timeout became a short one, and one value landed exactly on "wait forever". Out-of-range microseconds are refused rather than rounded

  • recvmsg() reports ancillary data it had to discard. With no room for it, or no buffer at all, the data was dropped and MSG_CTRUNC was not set, so the caller was told nothing was missing

  • A read with a zero-length buffer on a datagram socket discards the datagram, which is what that read is for. It returned zero and left the datagram queued

  • DeleteRouteTagList() reports a route that was not there. NetX Duo answers success without looking when its table is empty, so deleting a route that never existed failed on a machine with routes and succeeded on one without

  • ObtainSocket() does not lose the socket when it runs out of descriptors. The registry entry was removed and freed before the descriptor was allocated, and the failure path put back a different entry

  • A link-local address is chosen for the interface that needs it, on every attempt. The interface was picked when the address object was first built and never again, though stopping it only suspends it, so after an interface came or went it probed on the wrong one

  • The wait after a DHCP timeout is skipped when there is nothing to wait for. A failed link-local fallback was followed by fifteen seconds of waiting for an address that could not arrive

  • The release test installs on every card the archive supports, and boots each one. It had only ever booted the A2065, which is how an installer that could not detect five of the nine cards reached eight releases

  • A PCMCIA card is found on a machine with 8 MB of Fast RAM in the test rig. Zorro II Fast RAM covers 0x200000 to 0x9fffff and the A1200's PCMCIA windows are at 0x600000 and 0xa00000, so they collided and the driver reported no card while the slot reported one

Known issues

  • tests/tls/tls_handshake, tls_interop and tls_decompose do not build under -m68020 or -mcpu=68060: -ffunction-sections reaches LTRANS without -fno-optimize-sibling-calls, and the PC-relative branch check refuses the result. These are TEST images and are in no archive -- every shipped library and command builds clean for those processors, and the ones in this archive are the -m68000 build that runs on every 68k. docs/BACKLOG.md
  • The guru some machines see after NetShutdown is still unreproduced here.
  • CNet and CNet16 do not attach on real hardware, where the vendor cnet16.device does.
  • A live IPv6-only interface cannot be reconfigured or release its DHCPv6 lease.