+ }
+ return TRUE;
+}
+
+static void
+apn_fallback_trigger(proc_t proc)
+{
+ pid_t pid = 0;
+ struct kev_msg ev_msg;
+ struct kev_netevent_apnfallbk_data apnfallbk_data;
+
+ last_apn_fallback = net_uptime();
+ pid = proc_pid(proc);
+ uuid_t application_uuid;
+ uuid_clear(application_uuid);
+ proc_getexecutableuuid(proc, application_uuid,
+ sizeof(application_uuid));
+
+ bzero(&ev_msg, sizeof (struct kev_msg));
+ ev_msg.vendor_code = KEV_VENDOR_APPLE;
+ ev_msg.kev_class = KEV_NETWORK_CLASS;
+ ev_msg.kev_subclass = KEV_NETEVENT_SUBCLASS;
+ ev_msg.event_code = KEV_NETEVENT_APNFALLBACK;
+
+ bzero(&apnfallbk_data, sizeof(apnfallbk_data));
+ apnfallbk_data.epid = pid;
+ uuid_copy(apnfallbk_data.euuid, application_uuid);
+
+ ev_msg.dv[0].data_ptr = &apnfallbk_data;
+ ev_msg.dv[0].data_length = sizeof(apnfallbk_data);
+ kev_post_msg(&ev_msg);
+ apn_fallbk_log((LOG_INFO, "APN fallback notification issued.\n"));
+}
+
+/*
+ * Transform old in_pcbconnect() into an inner subroutine for new
+ * in_pcbconnect(); do some validity-checking on the remote address
+ * (in "nam") and then determine local host address (i.e., which
+ * interface) to use to access that remote host.
+ *
+ * This routine may alter the caller-supplied remote address "nam".
+ *
+ * The caller may override the bound-to-interface setting of the socket
+ * by specifying the ifscope parameter (e.g. from IP_PKTINFO.)
+ *
+ * This routine might return an ifp with a reference held if the caller
+ * provides a non-NULL outif, even in the error case. The caller is
+ * responsible for releasing its reference.
+ *
+ * Returns: 0 Success
+ * EINVAL Invalid argument
+ * EAFNOSUPPORT Address family not supported
+ * EADDRNOTAVAIL Address not available
+ */
+int
+in_pcbladdr(struct inpcb *inp, struct sockaddr *nam, struct in_addr *laddr,
+ unsigned int ifscope, struct ifnet **outif, int raw)
+{
+ struct route *ro = &inp->inp_route;
+ struct in_ifaddr *ia = NULL;
+ struct sockaddr_in sin;
+ int error = 0;
+ boolean_t restricted = FALSE;
+
+ if (outif != NULL)
+ *outif = NULL;
+ if (nam->sa_len != sizeof (struct sockaddr_in))
+ return (EINVAL);
+ if (SIN(nam)->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (raw == 0 && SIN(nam)->sin_port == 0)
+ return (EADDRNOTAVAIL);
+
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+ if (raw == 0 && (SIN(nam)->sin_addr.s_addr == INADDR_ANY ||
+ SIN(nam)->sin_addr.s_addr == (u_int32_t)INADDR_BROADCAST)) {
+ lck_rw_lock_shared(in_ifaddr_rwlock);
+ if (!TAILQ_EMPTY(&in_ifaddrhead)) {
+ ia = TAILQ_FIRST(&in_ifaddrhead);
+ IFA_LOCK_SPIN(&ia->ia_ifa);
+ if (SIN(nam)->sin_addr.s_addr == INADDR_ANY) {
+ SIN(nam)->sin_addr = IA_SIN(ia)->sin_addr;
+ } else if (ia->ia_ifp->if_flags & IFF_BROADCAST) {
+ SIN(nam)->sin_addr =
+ SIN(&ia->ia_broadaddr)->sin_addr;
+ }
+ IFA_UNLOCK(&ia->ia_ifa);
+ ia = NULL;
+ }
+ lck_rw_done(in_ifaddr_rwlock);
+ }
+ /*
+ * Otherwise, if the socket has already bound the source, just use it.
+ */
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ VERIFY(ia == NULL);
+ *laddr = inp->inp_laddr;
+ return (0);
+ }
+
+ /*
+ * If the ifscope is specified by the caller (e.g. IP_PKTINFO)
+ * then it overrides the sticky ifscope set for the socket.
+ */
+ if (ifscope == IFSCOPE_NONE && (inp->inp_flags & INP_BOUND_IF))
+ ifscope = inp->inp_boundifp->if_index;
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ * Note that we should check the address family of the cached
+ * destination, in case of sharing the cache with IPv6.
+ */
+ if (ro->ro_rt != NULL)
+ RT_LOCK_SPIN(ro->ro_rt);
+ if (ROUTE_UNUSABLE(ro) || ro->ro_dst.sa_family != AF_INET ||
+ SIN(&ro->ro_dst)->sin_addr.s_addr != SIN(nam)->sin_addr.s_addr ||
+ (inp->inp_socket->so_options & SO_DONTROUTE)) {
+ if (ro->ro_rt != NULL)
+ RT_UNLOCK(ro->ro_rt);
+ ROUTE_RELEASE(ro);
+ }
+ if (!(inp->inp_socket->so_options & SO_DONTROUTE) &&
+ (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
+ if (ro->ro_rt != NULL)
+ RT_UNLOCK(ro->ro_rt);
+ ROUTE_RELEASE(ro);
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof (struct sockaddr_in));
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof (struct sockaddr_in);
+ SIN(&ro->ro_dst)->sin_addr = SIN(nam)->sin_addr;
+ rtalloc_scoped(ro, ifscope);
+ if (ro->ro_rt != NULL)
+ RT_LOCK_SPIN(ro->ro_rt);
+ }
+ /* Sanitized local copy for interface address searches */
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof (struct sockaddr_in);
+ sin.sin_addr.s_addr = SIN(nam)->sin_addr.s_addr;
+ /*
+ * If we did not find (or use) a route, assume dest is reachable
+ * on a directly connected network and try to find a corresponding
+ * interface to take the source address from.
+ */
+ if (ro->ro_rt == NULL) {
+ proc_t proc = current_proc();
+
+ VERIFY(ia == NULL);
+ ia = ifatoia(ifa_ifwithdstaddr(SA(&sin)));
+ if (ia == NULL)
+ ia = ifatoia(ifa_ifwithnet_scoped(SA(&sin), ifscope));
+ error = ((ia == NULL) ? ENETUNREACH : 0);
+
+ if (apn_fallback_required(proc, inp->inp_socket,
+ (void *)nam))
+ apn_fallback_trigger(proc);
+
+ goto done;
+ }
+ RT_LOCK_ASSERT_HELD(ro->ro_rt);
+ /*
+ * If the outgoing interface on the route found is not
+ * a loopback interface, use the address from that interface.
+ */
+ if (!(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) {
+ VERIFY(ia == NULL);
+ /*
+ * If the route points to a cellular interface and the
+ * caller forbids our using interfaces of such type,
+ * pretend that there is no route.
+ * Apply the same logic for expensive interfaces.
+ */
+ if (inp_restricted_send(inp, ro->ro_rt->rt_ifp)) {
+ RT_UNLOCK(ro->ro_rt);
+ ROUTE_RELEASE(ro);
+ error = EHOSTUNREACH;
+ restricted = TRUE;
+ } else {
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro->ro_rt);
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ IFA_ADDREF(&ia->ia_ifa);
+ RT_UNLOCK(ro->ro_rt);
+ error = 0;
+ }
+ goto done;
+ }
+ VERIFY(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK);
+ RT_UNLOCK(ro->ro_rt);
+ /*
+ * The outgoing interface is marked with 'loopback net', so a route
+ * to ourselves is here.
+ * Try to find the interface of the destination address and then
+ * take the address from there. That interface is not necessarily
+ * a loopback interface.
+ */
+ VERIFY(ia == NULL);
+ ia = ifatoia(ifa_ifwithdstaddr(SA(&sin)));
+ if (ia == NULL)
+ ia = ifatoia(ifa_ifwithaddr_scoped(SA(&sin), ifscope));
+ if (ia == NULL)
+ ia = ifatoia(ifa_ifwithnet_scoped(SA(&sin), ifscope));
+ if (ia == NULL) {
+ RT_LOCK(ro->ro_rt);
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ if (ia != NULL)
+ IFA_ADDREF(&ia->ia_ifa);
+ RT_UNLOCK(ro->ro_rt);
+ }
+ error = ((ia == NULL) ? ENETUNREACH : 0);
+
+done:
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(SIN(nam)->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ IMO_LOCK(imo);
+ if (imo->imo_multicast_ifp != NULL && (ia == NULL ||
+ ia->ia_ifp != imo->imo_multicast_ifp)) {
+ ifp = imo->imo_multicast_ifp;
+ if (ia != NULL)
+ IFA_REMREF(&ia->ia_ifa);
+ lck_rw_lock_shared(in_ifaddr_rwlock);
+ TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
+ if (ia->ia_ifp == ifp)
+ break;
+ }
+ if (ia != NULL)
+ IFA_ADDREF(&ia->ia_ifa);
+ lck_rw_done(in_ifaddr_rwlock);
+ if (ia == NULL)
+ error = EADDRNOTAVAIL;
+ else
+ error = 0;
+ }
+ IMO_UNLOCK(imo);
+ }
+ /*
+ * Don't do pcblookup call here; return interface in laddr
+ * and exit to caller, that will do the lookup.
+ */
+ if (ia != NULL) {
+ /*
+ * If the source address belongs to a cellular interface
+ * and the socket forbids our using interfaces of such
+ * type, pretend that there is no source address.
+ * Apply the same logic for expensive interfaces.
+ */
+ IFA_LOCK_SPIN(&ia->ia_ifa);
+ if (inp_restricted_send(inp, ia->ia_ifa.ifa_ifp)) {
+ IFA_UNLOCK(&ia->ia_ifa);
+ error = EHOSTUNREACH;
+ restricted = TRUE;
+ } else if (error == 0) {
+ *laddr = ia->ia_addr.sin_addr;
+ if (outif != NULL) {
+ struct ifnet *ifp;
+
+ if (ro->ro_rt != NULL)
+ ifp = ro->ro_rt->rt_ifp;
+ else
+ ifp = ia->ia_ifp;
+
+ VERIFY(ifp != NULL);
+ IFA_CONVERT_LOCK(&ia->ia_ifa);
+ ifnet_reference(ifp); /* for caller */
+ if (*outif != NULL)
+ ifnet_release(*outif);
+ *outif = ifp;
+ }
+ IFA_UNLOCK(&ia->ia_ifa);
+ } else {
+ IFA_UNLOCK(&ia->ia_ifa);
+ }
+ IFA_REMREF(&ia->ia_ifa);
+ ia = NULL;
+ }