GithubHelp home page GithubHelp logo

haesbaert / mdnsd Goto Github PK

View Code? Open in Web Editor NEW
133.0 16.0 27.0 3.6 MB

Mdns daemon for OpenBSD.

Home Page: www.haesbaert.org/openmdns

Makefile 0.86% C 93.22% Roff 5.71% Shell 0.21%
c mdns mdns-stack openmdns openbsd

mdnsd's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mdnsd's Issues

reflector functionality

It would be nice to have reflector functionality like avahi so that received broadcasts on one interface go to all other interfaces allowed.

Permit port 0 in a service publication

Some services should, it seems, be served at the dummy port 0, but this is forbidden by mdnsctl.

I think that the ._device-info._tcp service is supposed to be served at port 0 (Macs, for example, advertise this service as a way of broadcasting information about the host). Unfortunately, I can't find chapter and verse which states this. However the following observations might be relevant:

  • This list post discusses _device-info._tcp, but without mentioning specifically the port number.
  • The dns-sd spec (p.27) mentions ‘ a placeholder SRV record (priority=0, weight=0, port=0, target host = host name of device)’; this is in a slightly different context, but does seem to indicate that a notional port 0 is a possibility (you'll be more familiar with the spec than I am).
  • Various ‘Avahi & Linux’ blog posts talk about the configuration stanza to get this service served at port 0. Of course, this type of post tends to have a strong air of voodoo about it, so might not be taken fully seriously.
  • Finally, the OS X dns-sd manpage mentions, under its -R documentation on ‘register a service’, that the port must be in the range 0 to 65535.

Port 0 is forbidden by mdnsctl because of the call:

res.port = strtonum(word, 1, UINT16_MAX, &errstr);

at line 309 of parser.c. Perhaps explicit

res.port = strtonum(word, 0, 65535, &errstr);

would be better.

"insane mtu" setting for loopback

Trying to run mdnsd on FreeBSD 11.2-RELEASE-p9 with a GENERIC Kernel, in a jail mounted on the loopback interface:

lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 172.16.10.10 netmask 0xfffffff0 
        groups: lo 

Results in this error:

# mdnsd -d lo1
startup
cache_insert: (new, cleaned up) (0x801c1c800) files [lo1:00:00:00:00:00:00]._workstation._tcp.local (SRV)
cache_insert: (new, cleaned up) (0x801c1cc00) files [lo1:00:00:00:00:00:00]._workstation._tcp.local (TXT)
using iface lo1 index 4
cache_insert: (new, cleaned up) (0x801c1d800) files.local (A)
cache_insert: (new, cleaned up) (0x801c1dc00) 10.10.16.172.in-addr.arpa (PTR)
cache_insert: (new, cleaned up) (0x801c1e000) files.local (HINFO)
opened raw socket with kernel on fd 5
mdns sock bound to 0.0.0.0:5353
if_fsm: event 'UP' resulted in action 'START' and changing state for interface lo1 from 'DOWN' to 'ACTIVE'
pkt_sendto: insane mtu
Can't send packet through lo1
can't send probe packet
pkt_sendto: insane mtu
Can't send packet through lo1
can't send probe packet
…
can't send announce packet

Changing the default mtu on the host works:

# ifconfig lo1 mtu 1500
# iocage console files
# ifconfig lo1
lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 1500
        options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 172.16.10.10 netmask 0xfffffff0 
        groups: lo 
# mdnsd -d lo1
startup
cache_insert: (new, cleaned up) (0x801c1c800) files [lo1:00:00:00:00:00:00]._workstation._tcp.local (SRV)
cache_insert: (new, cleaned up) (0x801c1cc00) files [lo1:00:00:00:00:00:00]._workstation._tcp.local (TXT)
using iface lo1 index 4
cache_insert: (new, cleaned up) (0x801c1d800) files.local (A)
cache_insert: (new, cleaned up) (0x801c1dc00) 10.10.16.172.in-addr.arpa (PTR)
cache_insert: (new, cleaned up) (0x801c1e000) files.local (HINFO)
opened raw socket with kernel on fd 5
mdns sock bound to 0.0.0.0:5353
if_fsm: event 'UP' resulted in action 'START' and changing state for interface lo1 from 'DOWN' to 'ACTIVE'

But, now all the other jails must use the lower mtu setting.

Supporting T_OPT (extended DNS)

I'd noticed lots of invalid packet classes (packet.c, pkt_parse_rr() checking rr->rrs.class) on my machine. Turns out this comes from T_OPT packets, which are different from others.

I'm still working on a patch, but in short, rr->rrs.type needs to be checked for T_OPT (41) before reading for cacheflush and rr->rrs.class. (I.e., after the dname read, which will be blank.) Otherwise, these values are not meaningful, as the T_OPT (RFC 2671) doesn't set the fields appropriately.

Allow multiple TXT records to be published on the same service

Hi,
I've built openmdnsd on FreeBSD (to replace avahi) and have found that I can't publish a service with two TXT records. In order to publish Apple's TimeMachine share on my NAS running netatalk I would normally use the avahi record:

<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">%h</name>
  <service>
    <type>_adisk._tcp</type>
    <port>9</port>
    <txt-record>sys=waMA=3c:d9:2b:04:2b:6f,adVF=0x100</txt-record>
    <txt-record>dk0=adVF=0xa1,adVN=tm,adVU=12D28B6D-AFD0-1D56-7161-723FBFE36EDC</txt-record>
  </service>
</service-group>

But doing the following with mdnsctl:

mdnsctl publish nas-01 adisk tcp 9 "sys=waMA=3c:d9:2b:04:2b:6f,adVF=0x100" "dk0=adVF=0xa1,adVN=tm,adVU=12D28B6D-AFD0-1D56-7161-723FBFE36EDC"

throws a superfluous argument error.
Running two mdnsctl commands with a TXT record in each throws a collision error.
Can you permit multiple TXT records on the command line?
Thanks.

pkg-readme addition

In the pkg-readme, it should also be mentioned that net.inet.ip.mforwarding needs to be enabled.

net.inet.ip6.mforwarding is also mentioned in the multicast(4) man page, but doesn't seem to exist (?).

On second thought, there may be a of rule I was missing, but the above sysctl did fix the issue for me. I will try to investigate further and report back.

Won't survive nmapping (DoS)

I ran a "Slow comprehensive scan" in Zenmap and mdnsd crashed in the result.

mdnsd -d:

using iface rl0 index 1
startup
publish_insert: type: A name: katarzyna.local
publish_insert: type: PTR name: 254.0.0.10.in-addr.arpa
publish_insert: type: HINFO name: katarzyna.local
opened raw socket with kernel on fd 5
mdns sock bound to 0.0.0.0:5353
if_fsm: event 'UP' resulted in action 'START' and changing state for interface rl0 from 'DOWN' to 'ACTIVE'
looking up premysl-laptop.local (A 1)
cache_lookup premysl-laptop.local (A)
query_fsm
control_send_rr (A) premysl-laptop.local
question_remove premysl-laptop.local (A)
pkt_parse_question: Non unicast question from 10.0.0.1:49644 with ephemeral source port, droping packet
pkt_parse_question: Non unicast question from 10.0.0.1:49900 with ephemeral source port, droping packet
pkt_parse_question: Non unicast question from 10.0.0.1:50156 with ephemeral source port, droping packet
pkt_parse_question: Non unicast question from 10.0.0.1:53378 with ephemeral source port, droping packet
pkt_parse_question: Non unicast question from 10.0.0.1:53378 with ephemeral source port, droping packet
Unknown 390
pkt_parse_rr: Unknown () Invalid packet class 8192
Can't parse AR RR
pkt_parse_question: Non unicast question from 10.0.0.1:53378 with ephemeral source port, droping packet
pkt_parse_question: Non unicast question from 10.0.0.1:53378 with ephemeral source port, droping packet
pkt_parse_header: bad packet size 3485373366
Segmentation fault 

mdnsd stops refreshing after a while [openbsd "tl" interface with autoconfprivacy enabled]

I can't tell yet whether this is an issue with mdnsd or with the OpenBSD "tl" ethernet driver, but after a number of IPv6 address changes (autoconf privacy enabled) mdnsd stops responding and does not appear to have refreshed for an extended period. If it is shut down it logs an error on attempting to write the "goodbye" messages:

Oct 31 12:20:21 pigeon mdnsd: cache_process: refresh Macadamia.local (AAAA)
Oct 31 12:26:32 pigeon mdnsd: cache_process: refresh silversword._device-info._tcp.local (TXT)
[... no mdnsd refresh entries, a few more errors from the shutdown omitted here ...]
Nov 1 09:30:25 pigeon mdnsd: rr_send_an error 85.42.168.192.in-addr.arpa (PTR)
Nov 1 09:30:25 pigeon mdnsd: rr_send_an error pigeon.local (HINFO)
Nov 1 09:30:25 pigeon mdnsd: terminating

turning off the IPv6 address refresh seems to stop the problem.

I'll continue to investigate.

mdnsd leaks memory [with patch to fix it]

I've found the mdnsd, at least as packaged for FreeBSD 12.1-RELEASE-p3,
SHA256 (haesbaert-mdnsd-0.7_GH0.tar.gz) = cb552f3431e57a3ad09b011f91ebdb8cc100a5c38978fcdb0fea27fc5ebd2880
SIZE (haesbaert-mdnsd-0.7_GH0.tar.gz) = 2935140

leaks memory until swap space fills and it crashes with a calloc() failure.

Running it on valgrind shows that the main leaks are of AN RR entries (packet.c:257), and AR RR entries (packet.c:281). I haven't tracked down what's in those RR entries that are lost.

Running mdnsd in the foreground I see a lot of pkt_parse_rr: edns0 'owner-option' and there are quite a few AN RRs of type T_A and T_AAAA which have 0 as the IP address. None of the counts obviously add up to the number of leaked blocks.

==16672== HEAP SUMMARY:
==16672==     in use at exit: 3,095,498 bytes in 3,539 blocks
==16672==   total heap usage: 11,050 allocs, 7,511 frees, 5,430,492 bytes allocated
==16672== 
==16672== 320 bytes in 1 blocks are definitely lost in loss record 21 of 42
==16672==    at 0x71E35B7: calloc (vg_replace_malloc.c:884)
==16672==    by 0x40DD87: pge_from_ms (mdns.c:766)
==16672==    by 0x40EA71: pge_new_workstation (mdns.c:1112)
==16672==    by 0x406A48: if_new (interface.c:437)
==16672==    by 0x404983: conf_init_ifaces (mdnsd.c:88)
==16672==    by 0x405002: main (mdnsd.c:324)
==16672== 
==16672== 320 bytes in 1 blocks are definitely lost in loss record 22 of 42
==16672==    at 0x71E35B7: calloc (vg_replace_malloc.c:884)
==16672==    by 0x40E786: pge_initprimary (mdns.c:1050)
==16672==    by 0x40500A: main (mdnsd.c:327)
==16672== 
==16672== 466,200 (257,520 direct, 208,680 indirect) bytes in 290 blocks are definitely lost in loss record 40 of 42
==16672==    at 0x71E35B7: calloc (vg_replace_malloc.c:884)
==16672==    by 0x406FE4: recv_packet (packet.c:281)
==16672==    by 0x7219B96: ??? (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x7215173: event_base_loop (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x7214CAA: event_dispatch (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x4050AE: main (mdnsd.c:359)
==16672== 
==16672== 2,430,456 (340,992 direct, 2,089,464 indirect) bytes in 384 blocks are definitely lost in loss record 42 of 42
==16672==    at 0x71E35B7: calloc (vg_replace_malloc.c:884)
==16672==    by 0x406EFB: recv_packet (packet.c:257)
==16672==    by 0x7219B96: ??? (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x7215173: event_base_loop (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x7214CAA: event_dispatch (in /usr/local/lib/libevent-2.1.so.7.0.0)
==16672==    by 0x4050AE: main (mdnsd.c:359)
==16672== 
==16672== LEAK SUMMARY:
==16672==    definitely lost: 599,152 bytes in 676 blocks
==16672==    indirectly lost: 2,298,144 bytes in 2,588 blocks
==16672==      possibly lost: 0 bytes in 0 blocks
==16672==    still reachable: 198,202 bytes in 275 blocks
==16672==         suppressed: 0 bytes in 0 blocks

log severity level too high for normal cache_process messages

In mdns.c, it seems that the "goodbye" and "refresh" messages should not be logged using log_warnx() which produces LOG_CRIT level messages. Since these are really an expected part of normal operations they should probably be logged with log_info() or log_debug().

IPv6 not implemented

Both the responder and resolver don't seem to be working over IPv6: the responder does not respond, and the resolver won't resolv AAAA records (there's actually no way to query them via CLI).

OpenBSD libc integration

Hey there,

You mention in one of your goals libc integration. What is the status of this? I couldn't find any reference to that in OBSD manpages.

Thanks,

Explain `mdnsctl publish` behaviour more fully in manpage

The manpage gives a brief example of using mdnsctl to publish a service. The example is perhaps a little too brief.

From the name, and from the (of course, only roughly standardised) behaviour of other fooctl tools on various unixes, I expected that the mdnsctl publish command would communicate with the running daemon, adjust its configuration, and exit. The manpage, and its publish example, did not make me think otherwise. When I saw the process ‘hanging’, I went to the mdnsctl source to confirm that the main() did indeed intend to be in a forever-loop.

Looking at closed issue #6 , I can see that I am not the only one to be confused about this.

It would be good if the manpage (i) gave a slightly fuller explanation, which mentioned that the process is supposed to ‘hang’, that (ii) it's useful to redirect the output, and that (iii) there will indeed be one process per published service, by design.

feature request: advertise (proxy) jail hostnames

It seems to be problematic to run mdnsd both in the host and in a jail: the jail instance takes over and the host is no longer accessible by its .local name. That makes sense because the IP stack is shared between host and jails. But mdnsctl proxy can only advertise services, not hosts. I would like to have a similar feature to advertise the jail hostname/IP address mappings, so that one instance is enough. Avahi for example does it if you have multiple hosts listed in /etc/avahi/hosts.

Build warnings

From GCC 7.0:

control.c: In function 'control_group_add':
control.c:340:13: warning: variable 'pg' set but not used [-Wunused-but-set-variable]
  struct pg *pg;
             ^~
packet.c: In function 'pkt_parse_rr':
packet.c:962:55: warning: variable 'pl' set but not used [-Wunused-but-set-variable]
  u_int16_t us, rdlen = 0, tmplen, i, code, plen, erc, pl;
                                                       ^~
packet.c:962:50: warning: variable 'erc' set but not used [-Wunused-but-set-variable]
  u_int16_t us, rdlen = 0, tmplen, i, code, plen, erc, pl;
                                                  ^~~
mdns.c: In function 'pge_kill':
mdns.c:959:13: warning: variable 'pg' set but not used [-Wunused-but-set-variable]
  struct pg *pg;
             ^~
mdnsd.c: In function 'fetchhinfo':
mdnsd.c:210:39: warning: '%s' directive output may be truncated writing up to 255 bytes into a region of size between 0 and 255 [-Wformat-truncation=]
  snprintf(hi->os, sizeof(hi->os), "%s %s", utsname.sysname,
                                       ^~
      utsname.release);
      ~~~~~~~                           
mdnsd.c:210:2: note: 'snprintf' output between 2 and 512 bytes into a destination of size 256
  snprintf(hi->os, sizeof(hi->os), "%s %s", utsname.sysname,
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      utsname.release);
      ~~~~~~~~~~~~~~~~

From scan-build 4.0:

mdnsl.c:347:4: warning: Value stored to 'r' is never read
                        r = mdns_handle_lookup(m, &rr, ev);
                        ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mdnsl.c:356:4: warning: Value stored to 'r' is never read
                        r = mdns_handle_browse(m, &rr, ev);
                        ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mdnsl.c:365:4: warning: Value stored to 'r' is never read
                        r = mdns_handle_resolve(m, &ms, ev);
                        ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mdnsl.c:383:4: warning: Value stored to 'r' is never read
                        r = mdns_handle_group(m, groupname, ev);
                        ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mdnsl.c:544:2: warning: Value stored to 'p' is never read
        p = start = namecp;
        ^   ~~~~~~~~~~~~~~
control.c:359:2: warning: Value stored to 'pg' is never read
        pg = pg_get(1, msg, c);
        ^    ~~~~~~~~~~~~~~~~~
packet.c:559:4: warning: Value stored to 'n' is never read
                        n    = 0;
                        ^      ~
packet.c:1167:7: warning: Use of memory after it is freed
                if (qst->flags & QST_FLAG_UNIRESP) {
                    ^~~~~~~~~~
packet.c:1296:3: warning: Value stored to 'len' is never read
                len -= INT16SZ;
                ^      ~~~~~~~
packet.c:1361:3: warning: Value stored to 'len' is never read
                len  -= oslen;
                ^       ~~~~~
packet.c:1375:3: warning: Value stored to 'len' is never read
                len  -= rdlen;
                ^       ~~~~~
packet.c:1386:3: warning: Value stored to 'len' is never read
                len  -= rdlen;
                ^       ~~~~~
packet.c:1411:3: warning: Value stored to 'len' is never read
                len   -= n;
                ^        ~
packet.c:1452:2: warning: Value stored to 'len' is never read
        len  -= n;
        ^       ~
mdns.c:485:3: warning: Use of memory after it is freed
                question_remove(rrs);
                ^~~~~~~~~~~~~~~~~~~~
mdns.c:961:2: warning: Value stored to 'pg' is never read
        pg = pge->pg;
        ^    ~~~~~~~
mdns.c:1129:3: warning: Use of memory after it is freed
                pge_kill(pge);
                ^~~~~~~~~~~~~

Write pidfile on startup

It would be nice if mdnsd wrote its PID to a /var/run file, so that the daemon can be conveniently killed within an rc script.

It would appear that calling pidfile(3) somewhere near the beginning of main would be sufficient to set this up rather nicely.

label compression is too simple

Hi!

I also found that label compression code compares only full labels, not parts of it. For example the second domain name in response with a single answer:
a._tcp.local PTR b._tcp.local
can be compressed like this:
0000000: 01 62 c0 0e
It won't even handle this case:
a.b._tcp.local PTR b._tcp.local

I've seen several mdns implementations and I wonder: why doesn't anybody use dn_expand() and dn_comp()? Is there something wrong with them? I think, they should be available on OpenBSD...

suggested fix for rr_rdata_cmp()

Hi!

Probably you should replace condition in rr_rdata_cmp() this way:
(RR_INADDRANY(rra) || RR_INADDRANY(rra)) -> (RR_INADDRANY(rra) || RR_INADDRANY(rrb))

mdnsctl publish "hangs"

When publishing a service with something like the below command, the prompt never appears again.

root@ap:~ # mdnsctl publish MySite http tcp 80 "WWW"
Group MySite is probing...
Group MySite is announcing...
Group MySite published.

Running openmdns on OpenBSD 5.7 GENERIC#837
Let me know if there is more information I can provide.

OpenBSD 6.1, mdnsd not receiving multicast packets?

I didn't have this problem on FreeBSD 11.0, but on OpenBSD 6.1 despite mdnsd's doing the proper IP_ADD_MEMBERSHIP, it doesn't receive any multicast packets. If you run "tcpdump -i port 5353" then the promiscuous mode will enable it to see the packets, but with "tcpdump -p -i port 5353" you don't see them. Hosts that cache the initial packets sent will see the system for a while, but then the cache entries time out and further queries are not answered.

I could easily believe this is something I've done wrong in my OpenBSD configuration, but I don't know what...

multicast=YES in /etc/rc.conf.local

ifconfig tl0
tl0: flags=208843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF6> mtu 1500
lladdr 00:80:5f:bd:53:49
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect (100baseTX full-duplex)
status: active
inet6 fe80::280:...%tl0 prefixlen 64 scopeid 0x1
inet6 2602:24a:... prefixlen 64 autoconf pltime 604764 vltime 2591964
inet6 2602:24a:... prefixlen 64 autoconf autoconfprivacy pltime 58831 vltime 577557
inet 192.168.42.88 netmask 0xffffff00 broadcast 192.168.42.255

./mdnsd -d tl0

malloc() warning: unknown char in MALLOC_OPTIONS
malloc() warning: unknown char in MALLOC_OPTIONS
startup
cache_insert: (new, cleaned up) (0x86ab1400) pigeon [tl0:00:80:5f:bd:53:49]._workstation._tcp.local (SRV)
cache_insert: (new, cleaned up) (0x840f3400) pigeon [tl0:00:80:5f:bd:53:49]._workstation._tcp.local (TXT)
using iface tl0 index 1
cache_insert: (new, cleaned up) (0x840f6000) pigeon.local (A)
cache_insert: (new, cleaned up) (0x86aac000) 88.42.168.192.in-addr.arpa (PTR)
cache_insert: (new, cleaned up) (0x86ab1800) pigeon.local (HINFO)
opened raw socket with kernel on fd 5
mdns sock bound to 0.0.0.0:5353
if_fsm: event 'UP' resulted in action 'START' and changing state for interface tl0 from 'DOWN' to 'ACTIVE'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.